mirror of https://github.com/hak5/openwrt.git
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;
|
|
+}
|
|
+
|