315 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 48e1af2bdd11204f11b3770a6c8d3eee64aee2e8 Mon Sep 17 00:00:00 2001
 | 
						|
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
 | 
						|
Date: Thu, 9 Oct 2008 21:01:46 -0700
 | 
						|
Subject: [PATCH 069/134] PM: Add user-space wake lock api.
 | 
						|
 | 
						|
This adds /sys/power/wake_lock and /sys/power/wake_unlock.
 | 
						|
Writing a string to wake_lock creates a wake lock the
 | 
						|
first time is sees a string and locks it. Optionally, the
 | 
						|
string can be followed by a timeout.
 | 
						|
To unlock the wake lock, write the same string to wake_unlock.
 | 
						|
---
 | 
						|
 kernel/power/Kconfig        |   10 ++
 | 
						|
 kernel/power/Makefile       |    1 +
 | 
						|
 kernel/power/main.c         |    9 ++
 | 
						|
 kernel/power/power.h        |   11 ++
 | 
						|
 kernel/power/userwakelock.c |  218 +++++++++++++++++++++++++++++++++++++++++++
 | 
						|
 5 files changed, 249 insertions(+), 0 deletions(-)
 | 
						|
 create mode 100644 kernel/power/userwakelock.c
 | 
						|
 | 
						|
--- a/kernel/power/Kconfig
 | 
						|
+++ b/kernel/power/Kconfig
 | 
						|
@@ -138,6 +138,16 @@ config WAKELOCK_STAT
 | 
						|
 	---help---
 | 
						|
 	  Report wake lock stats in /proc/wakelocks
 | 
						|
 
 | 
						|
+config USER_WAKELOCK
 | 
						|
+	bool "Userspace wake locks"
 | 
						|
+	depends on WAKELOCK
 | 
						|
+	default y
 | 
						|
+	---help---
 | 
						|
+	  User-space wake lock api. Write "lockname" or "lockname timeout"
 | 
						|
+	  to /sys/power/wake_lock lock and if needed create a wake lock.
 | 
						|
+	  Write "lockname" to /sys/power/wake_unlock to unlock a user wake
 | 
						|
+	  lock.
 | 
						|
+
 | 
						|
 config EARLYSUSPEND
 | 
						|
 	bool "Early suspend"
 | 
						|
 	depends on WAKELOCK
 | 
						|
--- 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_USER_WAKELOCK)	+= userwakelock.o
 | 
						|
 obj-$(CONFIG_EARLYSUSPEND)	+= earlysuspend.o
 | 
						|
 obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
 | 
						|
 
 | 
						|
--- a/kernel/power/main.c
 | 
						|
+++ b/kernel/power/main.c
 | 
						|
@@ -595,6 +595,11 @@ pm_trace_store(struct kobject *kobj, str
 | 
						|
 power_attr(pm_trace);
 | 
						|
 #endif /* CONFIG_PM_TRACE */
 | 
						|
 
 | 
						|
+#ifdef CONFIG_USER_WAKELOCK
 | 
						|
+power_attr(wake_lock);
 | 
						|
+power_attr(wake_unlock);
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 static struct attribute * g[] = {
 | 
						|
 	&state_attr.attr,
 | 
						|
 #ifdef CONFIG_PM_TRACE
 | 
						|
@@ -603,6 +608,10 @@ static struct attribute * g[] = {
 | 
						|
 #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
 | 
						|
 	&pm_test_attr.attr,
 | 
						|
 #endif
 | 
						|
+#ifdef CONFIG_USER_WAKELOCK
 | 
						|
+	&wake_lock_attr.attr,
 | 
						|
+	&wake_unlock_attr.attr,
 | 
						|
+#endif
 | 
						|
 	NULL,
 | 
						|
 };
 | 
						|
 
 | 
						|
--- a/kernel/power/power.h
 | 
						|
+++ b/kernel/power/power.h
 | 
						|
@@ -231,6 +231,17 @@ extern struct wake_lock main_wake_lock;
 | 
						|
 extern suspend_state_t requested_suspend_state;
 | 
						|
 #endif
 | 
						|
 
 | 
						|
+#ifdef CONFIG_USER_WAKELOCK
 | 
						|
+ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr,
 | 
						|
+			char *buf);
 | 
						|
+ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr,
 | 
						|
+			const char *buf, size_t n);
 | 
						|
+ssize_t wake_unlock_show(struct kobject *kobj, struct kobj_attribute *attr,
 | 
						|
+			char *buf);
 | 
						|
+ssize_t  wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr,
 | 
						|
+			const char *buf, size_t n);
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 #ifdef CONFIG_EARLYSUSPEND
 | 
						|
 /* kernel/power/earlysuspend.c */
 | 
						|
 void request_suspend_state(suspend_state_t state);
 | 
						|
--- /dev/null
 | 
						|
+++ b/kernel/power/userwakelock.c
 | 
						|
@@ -0,0 +1,218 @@
 | 
						|
+/* kernel/power/userwakelock.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/ctype.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/wakelock.h>
 | 
						|
+
 | 
						|
+#include "power.h"
 | 
						|
+
 | 
						|
+enum {
 | 
						|
+	DEBUG_FAILURE	= BIT(0),
 | 
						|
+	DEBUG_ERROR	= BIT(1),
 | 
						|
+	DEBUG_NEW	= BIT(2),
 | 
						|
+	DEBUG_ACCESS	= BIT(3),
 | 
						|
+	DEBUG_LOOKUP	= BIT(4),
 | 
						|
+};
 | 
						|
+static int debug_mask = DEBUG_FAILURE;
 | 
						|
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
 | 
						|
+
 | 
						|
+static DEFINE_MUTEX(tree_lock);
 | 
						|
+
 | 
						|
+struct user_wake_lock {
 | 
						|
+	struct rb_node		node;
 | 
						|
+	struct wake_lock	wake_lock;
 | 
						|
+	char			name[0];
 | 
						|
+};
 | 
						|
+struct rb_root user_wake_locks;
 | 
						|
+
 | 
						|
+static struct user_wake_lock *lookup_wake_lock_name(
 | 
						|
+	const char *buf, int allocate, long *timeoutptr)
 | 
						|
+{
 | 
						|
+	struct rb_node **p = &user_wake_locks.rb_node;
 | 
						|
+	struct rb_node *parent = NULL;
 | 
						|
+	struct user_wake_lock *l;
 | 
						|
+	int diff;
 | 
						|
+	u64 timeout;
 | 
						|
+	int name_len;
 | 
						|
+	const char *arg;
 | 
						|
+
 | 
						|
+	/* Find length of lock name and start of optional timeout string */
 | 
						|
+	arg = buf;
 | 
						|
+	while (*arg && !isspace(*arg))
 | 
						|
+		arg++;
 | 
						|
+	name_len = arg - buf;
 | 
						|
+	if (!name_len)
 | 
						|
+		goto bad_arg;
 | 
						|
+	while (isspace(*arg))
 | 
						|
+		arg++;
 | 
						|
+
 | 
						|
+	/* Process timeout string */
 | 
						|
+	if (timeoutptr && *arg) {
 | 
						|
+		timeout = simple_strtoull(arg, (char **)&arg, 0);
 | 
						|
+		while (isspace(*arg))
 | 
						|
+			arg++;
 | 
						|
+		if (*arg)
 | 
						|
+			goto bad_arg;
 | 
						|
+		/* convert timeout from nanoseconds to jiffies > 0 */
 | 
						|
+		timeout += (NSEC_PER_SEC / HZ) - 1;
 | 
						|
+		do_div(timeout, (NSEC_PER_SEC / HZ));
 | 
						|
+		if (timeout <= 0)
 | 
						|
+			timeout = 1;
 | 
						|
+		*timeoutptr = timeout;
 | 
						|
+	} else if (*arg)
 | 
						|
+		goto bad_arg;
 | 
						|
+	else if (timeoutptr)
 | 
						|
+		*timeoutptr = 0;
 | 
						|
+
 | 
						|
+	/* Lookup wake lock in rbtree */
 | 
						|
+	while (*p) {
 | 
						|
+		parent = *p;
 | 
						|
+		l = rb_entry(parent, struct user_wake_lock, node);
 | 
						|
+		diff = strncmp(buf, l->name, name_len);
 | 
						|
+		if (!diff && l->name[name_len])
 | 
						|
+			diff = -1;
 | 
						|
+		if (debug_mask & DEBUG_ERROR)
 | 
						|
+			pr_info("lookup_wake_lock_name: compare %.*s %s %d\n",
 | 
						|
+				name_len, buf, l->name, diff);
 | 
						|
+
 | 
						|
+		if (diff < 0)
 | 
						|
+			p = &(*p)->rb_left;
 | 
						|
+		else if (diff > 0)
 | 
						|
+			p = &(*p)->rb_right;
 | 
						|
+		else
 | 
						|
+			return l;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Allocate and add new wakelock to rbtree */
 | 
						|
+	if (!allocate) {
 | 
						|
+		if (debug_mask & DEBUG_ERROR)
 | 
						|
+			pr_info("lookup_wake_lock_name: %.*s not found\n",
 | 
						|
+				name_len, buf);
 | 
						|
+		return ERR_PTR(-EINVAL);
 | 
						|
+	}
 | 
						|
+	l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL);
 | 
						|
+	if (l == NULL) {
 | 
						|
+		if (debug_mask & DEBUG_FAILURE)
 | 
						|
+			pr_err("lookup_wake_lock_name: failed to allocate "
 | 
						|
+				"memory for %.*s\n", name_len, buf);
 | 
						|
+		return ERR_PTR(-ENOMEM);
 | 
						|
+	}
 | 
						|
+	memcpy(l->name, buf, name_len);
 | 
						|
+	if (debug_mask & DEBUG_NEW)
 | 
						|
+		pr_info("lookup_wake_lock_name: new wake lock %s\n", l->name);
 | 
						|
+	wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name);
 | 
						|
+	rb_link_node(&l->node, parent, p);
 | 
						|
+	rb_insert_color(&l->node, &user_wake_locks);
 | 
						|
+	return l;
 | 
						|
+
 | 
						|
+bad_arg:
 | 
						|
+	if (debug_mask & DEBUG_ERROR)
 | 
						|
+		pr_info("lookup_wake_lock_name: wake lock, %.*s, bad arg, %s\n",
 | 
						|
+			name_len, buf, arg);
 | 
						|
+	return ERR_PTR(-EINVAL);
 | 
						|
+}
 | 
						|
+
 | 
						|
+ssize_t wake_lock_show(
 | 
						|
+	struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 | 
						|
+{
 | 
						|
+	char *s = buf;
 | 
						|
+	char *end = buf + PAGE_SIZE;
 | 
						|
+	struct rb_node *n;
 | 
						|
+	struct user_wake_lock *l;
 | 
						|
+
 | 
						|
+	mutex_lock(&tree_lock);
 | 
						|
+
 | 
						|
+	for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
 | 
						|
+		l = rb_entry(n, struct user_wake_lock, node);
 | 
						|
+		if (wake_lock_active(&l->wake_lock))
 | 
						|
+			s += scnprintf(s, end - s, "%s ", l->name);
 | 
						|
+	}
 | 
						|
+	s += scnprintf(s, end - s, "\n");
 | 
						|
+
 | 
						|
+	mutex_unlock(&tree_lock);
 | 
						|
+	return (s - buf);
 | 
						|
+}
 | 
						|
+
 | 
						|
+ssize_t wake_lock_store(
 | 
						|
+	struct kobject *kobj, struct kobj_attribute *attr,
 | 
						|
+	const char *buf, size_t n)
 | 
						|
+{
 | 
						|
+	long timeout;
 | 
						|
+	struct user_wake_lock *l;
 | 
						|
+
 | 
						|
+	mutex_lock(&tree_lock);
 | 
						|
+	l = lookup_wake_lock_name(buf, 1, &timeout);
 | 
						|
+	if (IS_ERR(l)) {
 | 
						|
+		n = PTR_ERR(l);
 | 
						|
+		goto bad_name;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (debug_mask & DEBUG_ACCESS)
 | 
						|
+		pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);
 | 
						|
+
 | 
						|
+	if (timeout)
 | 
						|
+		wake_lock_timeout(&l->wake_lock, timeout);
 | 
						|
+	else
 | 
						|
+		wake_lock(&l->wake_lock);
 | 
						|
+bad_name:
 | 
						|
+	mutex_unlock(&tree_lock);
 | 
						|
+	return n;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+ssize_t wake_unlock_show(
 | 
						|
+	struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 | 
						|
+{
 | 
						|
+	char *s = buf;
 | 
						|
+	char *end = buf + PAGE_SIZE;
 | 
						|
+	struct rb_node *n;
 | 
						|
+	struct user_wake_lock *l;
 | 
						|
+
 | 
						|
+	mutex_lock(&tree_lock);
 | 
						|
+
 | 
						|
+	for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
 | 
						|
+		l = rb_entry(n, struct user_wake_lock, node);
 | 
						|
+		if (!wake_lock_active(&l->wake_lock))
 | 
						|
+			s += scnprintf(s, end - s, "%s ", l->name);
 | 
						|
+	}
 | 
						|
+	s += scnprintf(s, end - s, "\n");
 | 
						|
+
 | 
						|
+	mutex_unlock(&tree_lock);
 | 
						|
+	return (s - buf);
 | 
						|
+}
 | 
						|
+
 | 
						|
+ssize_t wake_unlock_store(
 | 
						|
+	struct kobject *kobj, struct kobj_attribute *attr,
 | 
						|
+	const char *buf, size_t n)
 | 
						|
+{
 | 
						|
+	struct user_wake_lock *l;
 | 
						|
+
 | 
						|
+	mutex_lock(&tree_lock);
 | 
						|
+	l = lookup_wake_lock_name(buf, 0, NULL);
 | 
						|
+	if (IS_ERR(l)) {
 | 
						|
+		n = PTR_ERR(l);
 | 
						|
+		goto not_found;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (debug_mask & DEBUG_ACCESS)
 | 
						|
+		pr_info("wake_unlock_store: %s\n", l->name);
 | 
						|
+
 | 
						|
+	wake_unlock(&l->wake_lock);
 | 
						|
+not_found:
 | 
						|
+	mutex_unlock(&tree_lock);
 | 
						|
+	return n;
 | 
						|
+}
 | 
						|
+
 |