Add Powersuspend 1.7

This commit is contained in:
DarkLord1731 2018-02-26 22:23:30 +01:00 committed by BlackMesa123
parent a3e8e4d08e
commit f940f4eed5
4 changed files with 374 additions and 0 deletions

View File

@ -0,0 +1,42 @@
/* include/linux/powersuspend.h
*
* Copyright (C) 2007-2008 Google, Inc.
* Copyright (C) 2013 Paul Reioux
*
* Modified by Jean-Pierre Rasquin <yank555.lu@gmail.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _LINUX_POWERSUSPEND_H
#define _LINUX_POWERSUSPEND_H
#include <linux/list.h>
#define POWER_SUSPEND_INACTIVE 0
#define POWER_SUSPEND_ACTIVE 1
#define POWER_SUSPEND_USERSPACE 1 // Use fauxclock as trigger
#define POWER_SUSPEND_PANEL 2 // Use display panel state as hook
struct power_suspend {
struct list_head link;
void (*suspend)(struct power_suspend *h);
void (*resume)(struct power_suspend *h);
};
void register_power_suspend(struct power_suspend *handler);
void unregister_power_suspend(struct power_suspend *handler);
void set_power_suspend_state_panel_hook(int new_state);
#endif

View File

@ -19,6 +19,19 @@ config SUSPEND_FREEZER
Turning OFF this setting is NOT recommended! If in doubt, say Y.
config POWERSUSPEND
bool "Power suspend"
default n
---help---
Call early suspend handlers when the user requested sleep state
changes.
config POWERSUSPEND_DEBUG
bool "Powersuspend driver debugging code"
depends on POWERSUSPEND
help
Output debugging info in dmesg [POWERSUSPEND] (Yank555.lu)
config SUSPEND_SKIP_SYNC
bool "Skip kernel's sys_sync() on suspend to RAM/standby"
depends on SUSPEND

View File

@ -8,6 +8,7 @@ obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_SUSPEND) += suspend.o
obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o
obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o
obj-$(CONFIG_POWERSUSPEND) += powersuspend.o
obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o
obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o

318
kernel/power/powersuspend.c Normal file
View File

@ -0,0 +1,318 @@
/* kernel/power/powersuspend.c
*
* Copyright (C) 2005-2008 Google, Inc.
* Copyright (C) 2013 Paul Reioux
*
* Modified by Jean-Pierre Rasquin <yank555.lu@gmail.com>
*
* v1.1 - make powersuspend not depend on a userspace initiator anymore,
* but use a hook in autosleep instead.
*
* v1.2 - make kernel / userspace mode switchable
*
* v1.3 - add a hook in display panel driver as alternative kernel trigger
*
* v1.4 - add a hybrid-kernel mode, accepting both kernel hooks (first wins)
*
* v1.5 - fix hybrid-kernel mode cannot be set through sysfs
*
* v1.6 - remove autosleep and hybrid modes (autosleep not working on shamu)
*
* v1.7 - do only run state change if change actually requests a new state
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/powersuspend.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#define MAJOR_VERSION 1
#define MINOR_VERSION 7
struct workqueue_struct *suspend_work_queue;
static DEFINE_MUTEX(power_suspend_lock);
static LIST_HEAD(power_suspend_handlers);
static void power_suspend(struct work_struct *work);
static void power_resume(struct work_struct *work);
static DECLARE_WORK(power_suspend_work, power_suspend);
static DECLARE_WORK(power_resume_work, power_resume);
static DEFINE_SPINLOCK(state_lock);
static int state; // Yank555.lu : Current powersave state (screen on / off)
static int mode; // Yank555.lu : Current powersave mode (userspace / panel)
void register_power_suspend(struct power_suspend *handler)
{
struct list_head *pos;
mutex_lock(&power_suspend_lock);
list_for_each(pos, &power_suspend_handlers) {
struct power_suspend *p;
p = list_entry(pos, struct power_suspend, link);
}
list_add_tail(&handler->link, pos);
mutex_unlock(&power_suspend_lock);
}
EXPORT_SYMBOL(register_power_suspend);
void unregister_power_suspend(struct power_suspend *handler)
{
mutex_lock(&power_suspend_lock);
list_del(&handler->link);
mutex_unlock(&power_suspend_lock);
}
EXPORT_SYMBOL(unregister_power_suspend);
static void power_suspend(struct work_struct *work)
{
struct power_suspend *pos;
unsigned long irqflags;
int abort = 0;
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] entering suspend...\n");
#endif
mutex_lock(&power_suspend_lock);
spin_lock_irqsave(&state_lock, irqflags);
if (state == POWER_SUSPEND_INACTIVE)
abort = 1;
spin_unlock_irqrestore(&state_lock, irqflags);
if (abort)
goto abort_suspend;
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] suspending...\n");
#endif
list_for_each_entry(pos, &power_suspend_handlers, link) {
if (pos->suspend != NULL) {
pos->suspend(pos);
}
}
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] suspend completed.\n");
#endif
abort_suspend:
mutex_unlock(&power_suspend_lock);
}
static void power_resume(struct work_struct *work)
{
struct power_suspend *pos;
unsigned long irqflags;
int abort = 0;
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] entering resume...\n");
#endif
mutex_lock(&power_suspend_lock);
spin_lock_irqsave(&state_lock, irqflags);
if (state == POWER_SUSPEND_ACTIVE)
abort = 1;
spin_unlock_irqrestore(&state_lock, irqflags);
if (abort)
goto abort_resume;
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] resuming...\n");
#endif
list_for_each_entry_reverse(pos, &power_suspend_handlers, link) {
if (pos->resume != NULL) {
pos->resume(pos);
}
}
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] resume completed.\n");
#endif
abort_resume:
mutex_unlock(&power_suspend_lock);
}
void set_power_suspend_state(int new_state)
{
unsigned long irqflags;
if (state != new_state) {
spin_lock_irqsave(&state_lock, irqflags);
if (state == POWER_SUSPEND_INACTIVE && new_state == POWER_SUSPEND_ACTIVE) {
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] state activated.\n");
#endif
state = new_state;
queue_work(suspend_work_queue, &power_suspend_work);
} else if (state == POWER_SUSPEND_ACTIVE && new_state == POWER_SUSPEND_INACTIVE) {
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] state deactivated.\n");
#endif
state = new_state;
queue_work(suspend_work_queue, &power_resume_work);
}
spin_unlock_irqrestore(&state_lock, irqflags);
#ifdef CONFIG_POWERSUSPEND_DEBUG
} else {
pr_info("[POWERSUSPEND] state change requested, but unchanged ?! Ignored !\n");
#endif
}
}
void set_power_suspend_state_panel_hook(int new_state)
{
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] panel resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup");
#endif
// Yank555.lu : Only allow panel hook changes in panel mode
if (mode == POWER_SUSPEND_PANEL)
set_power_suspend_state(new_state);
}
EXPORT_SYMBOL(set_power_suspend_state_panel_hook);
// ------------------------------------------ sysfs interface ------------------------------------------
static ssize_t power_suspend_state_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%u\n", state);
}
static ssize_t power_suspend_state_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int new_state = 0;
// Yank555.lu : Only allow sysfs changes from userspace mode
if (mode != POWER_SUSPEND_USERSPACE)
return -EINVAL;
sscanf(buf, "%d\n", &new_state);
#ifdef CONFIG_POWERSUSPEND_DEBUG
pr_info("[POWERSUSPEND] userspace resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup");
#endif
if(new_state == POWER_SUSPEND_ACTIVE || new_state == POWER_SUSPEND_INACTIVE)
set_power_suspend_state(new_state);
return count;
}
static struct kobj_attribute power_suspend_state_attribute =
__ATTR(power_suspend_state, 0660,
power_suspend_state_show,
power_suspend_state_store);
static ssize_t power_suspend_mode_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%u\n", mode);
}
static ssize_t power_suspend_mode_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int data = 0;
sscanf(buf, "%d\n", &data);
switch (data) {
case POWER_SUSPEND_PANEL:
case POWER_SUSPEND_USERSPACE: mode = data;
return count;
default:
return -EINVAL;
}
}
static struct kobj_attribute power_suspend_mode_attribute =
__ATTR(power_suspend_mode, 0660,
power_suspend_mode_show,
power_suspend_mode_store);
static ssize_t power_suspend_version_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "version: %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
}
static struct kobj_attribute power_suspend_version_attribute =
__ATTR(power_suspend_version, 0444,
power_suspend_version_show,
NULL);
static struct attribute *power_suspend_attrs[] =
{
&power_suspend_state_attribute.attr,
&power_suspend_mode_attribute.attr,
&power_suspend_version_attribute.attr,
NULL,
};
static struct attribute_group power_suspend_attr_group =
{
.attrs = power_suspend_attrs,
};
static struct kobject *power_suspend_kobj;
// ------------------ sysfs interface -----------------------
static int __init power_suspend_init(void)
{
int sysfs_result;
power_suspend_kobj = kobject_create_and_add("power_suspend",
kernel_kobj);
if (!power_suspend_kobj) {
pr_err("%s kobject create failed!\n", __FUNCTION__);
return -ENOMEM;
}
sysfs_result = sysfs_create_group(power_suspend_kobj,
&power_suspend_attr_group);
if (sysfs_result) {
pr_info("%s group create failed!\n", __FUNCTION__);
kobject_put(power_suspend_kobj);
return -ENOMEM;
}
suspend_work_queue = create_singlethread_workqueue("p-suspend");
if (suspend_work_queue == NULL) {
return -ENOMEM;
}
// mode = POWER_SUSPEND_USERSPACE; // Yank555.lu : Default to userspace mode
mode = POWER_SUSPEND_PANEL; // Yank555.lu : Default to display panel mode
return 0;
}
static void __exit power_suspend_exit(void)
{
if (power_suspend_kobj != NULL)
kobject_put(power_suspend_kobj);
destroy_workqueue(suspend_work_queue);
}
core_initcall(power_suspend_init);
module_exit(power_suspend_exit);
MODULE_AUTHOR("Paul Reioux <reioux@gmail.com> / Jean-Pierre Rasquin <yank555.lu@gmail.com>");
MODULE_DESCRIPTION("power_suspend - A replacement kernel PM driver for"
"Android's deprecated early_suspend/late_resume PM driver!");
MODULE_LICENSE("GPL v2");