245 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 2d4a25785ad1aa51bad967543e1ab20e9ed4a046 Mon Sep 17 00:00:00 2001
 | 
						|
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
 | 
						|
Date: Tue, 7 Oct 2008 20:48:01 -0700
 | 
						|
Subject: [PATCH 067/134] PM: Implement early suspend api
 | 
						|
 | 
						|
---
 | 
						|
 kernel/power/Kconfig        |   12 +++
 | 
						|
 kernel/power/Makefile       |    1 +
 | 
						|
 kernel/power/earlysuspend.c |  178 +++++++++++++++++++++++++++++++++++++++++++
 | 
						|
 kernel/power/power.h        |    6 ++
 | 
						|
 4 files changed, 197 insertions(+), 0 deletions(-)
 | 
						|
 create mode 100644 kernel/power/earlysuspend.c
 | 
						|
 | 
						|
--- a/kernel/power/Kconfig
 | 
						|
+++ b/kernel/power/Kconfig
 | 
						|
@@ -119,6 +119,9 @@ config SUSPEND_FREEZER
 | 
						|
 config HAS_WAKELOCK
 | 
						|
 	bool
 | 
						|
 
 | 
						|
+config HAS_EARLYSUSPEND
 | 
						|
+	bool
 | 
						|
+
 | 
						|
 config WAKELOCK
 | 
						|
 	bool "Wake lock"
 | 
						|
 	depends on PM && RTC_CLASS
 | 
						|
@@ -135,6 +138,15 @@ config WAKELOCK_STAT
 | 
						|
 	---help---
 | 
						|
 	  Report wake lock stats in /proc/wakelocks
 | 
						|
 
 | 
						|
+config EARLYSUSPEND
 | 
						|
+	bool "Early suspend"
 | 
						|
+	depends on WAKELOCK
 | 
						|
+	default y
 | 
						|
+	select HAS_EARLYSUSPEND
 | 
						|
+	---help---
 | 
						|
+	  Call early suspend handlers when the user requested sleep state
 | 
						|
+	  changes.
 | 
						|
+
 | 
						|
 config HIBERNATION
 | 
						|
 	bool "Hibernation (aka 'suspend to disk')"
 | 
						|
 	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
 | 
						|
--- a/kernel/power/Makefile
 | 
						|
+++ b/kernel/power/Makefile
 | 
						|
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM)		+= main.o
 | 
						|
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 | 
						|
 obj-$(CONFIG_FREEZER)		+= process.o
 | 
						|
 obj-$(CONFIG_WAKELOCK)		+= wakelock.o
 | 
						|
+obj-$(CONFIG_EARLYSUSPEND)	+= earlysuspend.o
 | 
						|
 obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
 | 
						|
 
 | 
						|
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
 | 
						|
--- /dev/null
 | 
						|
+++ b/kernel/power/earlysuspend.c
 | 
						|
@@ -0,0 +1,178 @@
 | 
						|
+/* kernel/power/earlysuspend.c
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2005-2008 Google, Inc.
 | 
						|
+ *
 | 
						|
+ * 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/earlysuspend.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/mutex.h>
 | 
						|
+#include <linux/rtc.h>
 | 
						|
+#include <linux/syscalls.h> /* sys_sync */
 | 
						|
+#include <linux/wakelock.h>
 | 
						|
+#include <linux/workqueue.h>
 | 
						|
+
 | 
						|
+#include "power.h"
 | 
						|
+
 | 
						|
+enum {
 | 
						|
+	DEBUG_USER_STATE = 1U << 0,
 | 
						|
+	DEBUG_SUSPEND = 1U << 2,
 | 
						|
+};
 | 
						|
+static int debug_mask = DEBUG_USER_STATE;
 | 
						|
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
 | 
						|
+
 | 
						|
+static DEFINE_MUTEX(early_suspend_lock);
 | 
						|
+static LIST_HEAD(early_suspend_handlers);
 | 
						|
+static void early_suspend(struct work_struct *work);
 | 
						|
+static void late_resume(struct work_struct *work);
 | 
						|
+static DECLARE_WORK(early_suspend_work, early_suspend);
 | 
						|
+static DECLARE_WORK(late_resume_work, late_resume);
 | 
						|
+static DEFINE_SPINLOCK(state_lock);
 | 
						|
+enum {
 | 
						|
+	SUSPEND_REQUESTED = 0x1,
 | 
						|
+	SUSPENDED = 0x2,
 | 
						|
+	SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
 | 
						|
+};
 | 
						|
+static int state;
 | 
						|
+
 | 
						|
+void register_early_suspend(struct early_suspend *handler)
 | 
						|
+{
 | 
						|
+	struct list_head *pos;
 | 
						|
+
 | 
						|
+	mutex_lock(&early_suspend_lock);
 | 
						|
+	list_for_each(pos, &early_suspend_handlers) {
 | 
						|
+		struct early_suspend *e;
 | 
						|
+		e = list_entry(pos, struct early_suspend, link);
 | 
						|
+		if (e->level > handler->level)
 | 
						|
+			break;
 | 
						|
+	}
 | 
						|
+	list_add_tail(&handler->link, pos);
 | 
						|
+	if ((state & SUSPENDED) && handler->suspend)
 | 
						|
+		handler->suspend(handler);
 | 
						|
+	mutex_unlock(&early_suspend_lock);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(register_early_suspend);
 | 
						|
+
 | 
						|
+void unregister_early_suspend(struct early_suspend *handler)
 | 
						|
+{
 | 
						|
+	mutex_lock(&early_suspend_lock);
 | 
						|
+	list_del(&handler->link);
 | 
						|
+	mutex_unlock(&early_suspend_lock);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(unregister_early_suspend);
 | 
						|
+
 | 
						|
+static void early_suspend(struct work_struct *work)
 | 
						|
+{
 | 
						|
+	struct early_suspend *pos;
 | 
						|
+	unsigned long irqflags;
 | 
						|
+	int abort = 0;
 | 
						|
+
 | 
						|
+	mutex_lock(&early_suspend_lock);
 | 
						|
+	spin_lock_irqsave(&state_lock, irqflags);
 | 
						|
+	if (state == SUSPEND_REQUESTED)
 | 
						|
+		state |= SUSPENDED;
 | 
						|
+	else
 | 
						|
+		abort = 1;
 | 
						|
+	spin_unlock_irqrestore(&state_lock, irqflags);
 | 
						|
+
 | 
						|
+	if (abort) {
 | 
						|
+		if (debug_mask & DEBUG_SUSPEND)
 | 
						|
+			pr_info("early_suspend: abort, state %d\n", state);
 | 
						|
+		mutex_unlock(&early_suspend_lock);
 | 
						|
+		goto abort;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (debug_mask & DEBUG_SUSPEND)
 | 
						|
+		pr_info("early_suspend: call handlers\n");
 | 
						|
+	list_for_each_entry(pos, &early_suspend_handlers, link) {
 | 
						|
+		if (pos->suspend != NULL)
 | 
						|
+			pos->suspend(pos);
 | 
						|
+	}
 | 
						|
+	mutex_unlock(&early_suspend_lock);
 | 
						|
+
 | 
						|
+	if (debug_mask & DEBUG_SUSPEND)
 | 
						|
+		pr_info("early_suspend: sync\n");
 | 
						|
+
 | 
						|
+	sys_sync();
 | 
						|
+abort:
 | 
						|
+	spin_lock_irqsave(&state_lock, irqflags);
 | 
						|
+	if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
 | 
						|
+		wake_unlock(&main_wake_lock);
 | 
						|
+	spin_unlock_irqrestore(&state_lock, irqflags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void late_resume(struct work_struct *work)
 | 
						|
+{
 | 
						|
+	struct early_suspend *pos;
 | 
						|
+	unsigned long irqflags;
 | 
						|
+	int abort = 0;
 | 
						|
+
 | 
						|
+	mutex_lock(&early_suspend_lock);
 | 
						|
+	spin_lock_irqsave(&state_lock, irqflags);
 | 
						|
+	if (state == SUSPENDED)
 | 
						|
+		state &= ~SUSPENDED;
 | 
						|
+	else
 | 
						|
+		abort = 1;
 | 
						|
+	spin_unlock_irqrestore(&state_lock, irqflags);
 | 
						|
+
 | 
						|
+	if (abort) {
 | 
						|
+		if (debug_mask & DEBUG_SUSPEND)
 | 
						|
+			pr_info("late_resume: abort, state %d\n", state);
 | 
						|
+		goto abort;
 | 
						|
+	}
 | 
						|
+	if (debug_mask & DEBUG_SUSPEND)
 | 
						|
+		pr_info("late_resume: call handlers\n");
 | 
						|
+	list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
 | 
						|
+		if (pos->resume != NULL)
 | 
						|
+			pos->resume(pos);
 | 
						|
+	if (debug_mask & DEBUG_SUSPEND)
 | 
						|
+		pr_info("late_resume: done\n");
 | 
						|
+abort:
 | 
						|
+	mutex_unlock(&early_suspend_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+void request_suspend_state(suspend_state_t new_state)
 | 
						|
+{
 | 
						|
+	unsigned long irqflags;
 | 
						|
+	int old_sleep;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&state_lock, irqflags);
 | 
						|
+	old_sleep = state & SUSPEND_REQUESTED;
 | 
						|
+	if (debug_mask & DEBUG_USER_STATE) {
 | 
						|
+		struct timespec ts;
 | 
						|
+		struct rtc_time tm;
 | 
						|
+		getnstimeofday(&ts);
 | 
						|
+		rtc_time_to_tm(ts.tv_sec, &tm);
 | 
						|
+		pr_info("request_suspend_state: %s (%d->%d) at %lld "
 | 
						|
+			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
 | 
						|
+			new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
 | 
						|
+			requested_suspend_state, new_state,
 | 
						|
+			ktime_to_ns(ktime_get()),
 | 
						|
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 | 
						|
+			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
 | 
						|
+	}
 | 
						|
+	if (!old_sleep && new_state != PM_SUSPEND_ON) {
 | 
						|
+		state |= SUSPEND_REQUESTED;
 | 
						|
+		queue_work(suspend_work_queue, &early_suspend_work);
 | 
						|
+	} else if (old_sleep && new_state == PM_SUSPEND_ON) {
 | 
						|
+		state &= ~SUSPEND_REQUESTED;
 | 
						|
+		wake_lock(&main_wake_lock);
 | 
						|
+		queue_work(suspend_work_queue, &late_resume_work);
 | 
						|
+	}
 | 
						|
+	requested_suspend_state = new_state;
 | 
						|
+	spin_unlock_irqrestore(&state_lock, irqflags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+suspend_state_t get_suspend_state(void)
 | 
						|
+{
 | 
						|
+	return requested_suspend_state;
 | 
						|
+}
 | 
						|
--- a/kernel/power/power.h
 | 
						|
+++ b/kernel/power/power.h
 | 
						|
@@ -230,3 +230,9 @@ extern struct workqueue_struct *suspend_
 | 
						|
 extern struct wake_lock main_wake_lock;
 | 
						|
 extern suspend_state_t requested_suspend_state;
 | 
						|
 #endif
 | 
						|
+
 | 
						|
+#ifdef CONFIG_EARLYSUSPEND
 | 
						|
+/* kernel/power/earlysuspend.c */
 | 
						|
+void request_suspend_state(suspend_state_t state);
 | 
						|
+suspend_state_t get_suspend_state(void);
 | 
						|
+#endif
 |