diff options
Diffstat (limited to 'target/linux/goldfish/patches-2.6.30/0069-PM-Add-user-space-wake-lock-api.patch')
-rw-r--r-- | target/linux/goldfish/patches-2.6.30/0069-PM-Add-user-space-wake-lock-api.patch | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/target/linux/goldfish/patches-2.6.30/0069-PM-Add-user-space-wake-lock-api.patch b/target/linux/goldfish/patches-2.6.30/0069-PM-Add-user-space-wake-lock-api.patch new file mode 100644 index 0000000000..c1d0125f11 --- /dev/null +++ b/target/linux/goldfish/patches-2.6.30/0069-PM-Add-user-space-wake-lock-api.patch @@ -0,0 +1,314 @@ +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; ++} ++ |