| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- From 48e1af2bdd11204f11b3770a6c8d3eee64aee2e8 Mon Sep 17 00:00:00 2001
- From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <[email protected]>
- 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;
- +}
- +
|