aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power/userwakelock.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/power/userwakelock.c')
-rw-r--r--kernel/power/userwakelock.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/kernel/power/userwakelock.c b/kernel/power/userwakelock.c
new file mode 100644
index 00000000000..a28a8db4146
--- /dev/null
+++ b/kernel/power/userwakelock.c
@@ -0,0 +1,219 @@
1/* kernel/power/userwakelock.c
2 *
3 * Copyright (C) 2005-2008 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/ctype.h>
17#include <linux/module.h>
18#include <linux/wakelock.h>
19#include <linux/slab.h>
20
21#include "power.h"
22
23enum {
24 DEBUG_FAILURE = BIT(0),
25 DEBUG_ERROR = BIT(1),
26 DEBUG_NEW = BIT(2),
27 DEBUG_ACCESS = BIT(3),
28 DEBUG_LOOKUP = BIT(4),
29};
30static int debug_mask = DEBUG_FAILURE;
31module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
32
33static DEFINE_MUTEX(tree_lock);
34
35struct user_wake_lock {
36 struct rb_node node;
37 struct wake_lock wake_lock;
38 char name[0];
39};
40struct rb_root user_wake_locks;
41
42static struct user_wake_lock *lookup_wake_lock_name(
43 const char *buf, int allocate, long *timeoutptr)
44{
45 struct rb_node **p = &user_wake_locks.rb_node;
46 struct rb_node *parent = NULL;
47 struct user_wake_lock *l;
48 int diff;
49 u64 timeout;
50 int name_len;
51 const char *arg;
52
53 /* Find length of lock name and start of optional timeout string */
54 arg = buf;
55 while (*arg && !isspace(*arg))
56 arg++;
57 name_len = arg - buf;
58 if (!name_len)
59 goto bad_arg;
60 while (isspace(*arg))
61 arg++;
62
63 /* Process timeout string */
64 if (timeoutptr && *arg) {
65 timeout = simple_strtoull(arg, (char **)&arg, 0);
66 while (isspace(*arg))
67 arg++;
68 if (*arg)
69 goto bad_arg;
70 /* convert timeout from nanoseconds to jiffies > 0 */
71 timeout += (NSEC_PER_SEC / HZ) - 1;
72 do_div(timeout, (NSEC_PER_SEC / HZ));
73 if (timeout <= 0)
74 timeout = 1;
75 *timeoutptr = timeout;
76 } else if (*arg)
77 goto bad_arg;
78 else if (timeoutptr)
79 *timeoutptr = 0;
80
81 /* Lookup wake lock in rbtree */
82 while (*p) {
83 parent = *p;
84 l = rb_entry(parent, struct user_wake_lock, node);
85 diff = strncmp(buf, l->name, name_len);
86 if (!diff && l->name[name_len])
87 diff = -1;
88 if (debug_mask & DEBUG_ERROR)
89 pr_info("lookup_wake_lock_name: compare %.*s %s %d\n",
90 name_len, buf, l->name, diff);
91
92 if (diff < 0)
93 p = &(*p)->rb_left;
94 else if (diff > 0)
95 p = &(*p)->rb_right;
96 else
97 return l;
98 }
99
100 /* Allocate and add new wakelock to rbtree */
101 if (!allocate) {
102 if (debug_mask & DEBUG_ERROR)
103 pr_info("lookup_wake_lock_name: %.*s not found\n",
104 name_len, buf);
105 return ERR_PTR(-EINVAL);
106 }
107 l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL);
108 if (l == NULL) {
109 if (debug_mask & DEBUG_FAILURE)
110 pr_err("lookup_wake_lock_name: failed to allocate "
111 "memory for %.*s\n", name_len, buf);
112 return ERR_PTR(-ENOMEM);
113 }
114 memcpy(l->name, buf, name_len);
115 if (debug_mask & DEBUG_NEW)
116 pr_info("lookup_wake_lock_name: new wake lock %s\n", l->name);
117 wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name);
118 rb_link_node(&l->node, parent, p);
119 rb_insert_color(&l->node, &user_wake_locks);
120 return l;
121
122bad_arg:
123 if (debug_mask & DEBUG_ERROR)
124 pr_info("lookup_wake_lock_name: wake lock, %.*s, bad arg, %s\n",
125 name_len, buf, arg);
126 return ERR_PTR(-EINVAL);
127}
128
129ssize_t wake_lock_show(
130 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
131{
132 char *s = buf;
133 char *end = buf + PAGE_SIZE;
134 struct rb_node *n;
135 struct user_wake_lock *l;
136
137 mutex_lock(&tree_lock);
138
139 for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
140 l = rb_entry(n, struct user_wake_lock, node);
141 if (wake_lock_active(&l->wake_lock))
142 s += scnprintf(s, end - s, "%s ", l->name);
143 }
144 s += scnprintf(s, end - s, "\n");
145
146 mutex_unlock(&tree_lock);
147 return (s - buf);
148}
149
150ssize_t wake_lock_store(
151 struct kobject *kobj, struct kobj_attribute *attr,
152 const char *buf, size_t n)
153{
154 long timeout;
155 struct user_wake_lock *l;
156
157 mutex_lock(&tree_lock);
158 l = lookup_wake_lock_name(buf, 1, &timeout);
159 if (IS_ERR(l)) {
160 n = PTR_ERR(l);
161 goto bad_name;
162 }
163
164 if (debug_mask & DEBUG_ACCESS)
165 pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);
166
167 if (timeout)
168 wake_lock_timeout(&l->wake_lock, timeout);
169 else
170 wake_lock(&l->wake_lock);
171bad_name:
172 mutex_unlock(&tree_lock);
173 return n;
174}
175
176
177ssize_t wake_unlock_show(
178 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
179{
180 char *s = buf;
181 char *end = buf + PAGE_SIZE;
182 struct rb_node *n;
183 struct user_wake_lock *l;
184
185 mutex_lock(&tree_lock);
186
187 for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
188 l = rb_entry(n, struct user_wake_lock, node);
189 if (!wake_lock_active(&l->wake_lock))
190 s += scnprintf(s, end - s, "%s ", l->name);
191 }
192 s += scnprintf(s, end - s, "\n");
193
194 mutex_unlock(&tree_lock);
195 return (s - buf);
196}
197
198ssize_t wake_unlock_store(
199 struct kobject *kobj, struct kobj_attribute *attr,
200 const char *buf, size_t n)
201{
202 struct user_wake_lock *l;
203
204 mutex_lock(&tree_lock);
205 l = lookup_wake_lock_name(buf, 0, NULL);
206 if (IS_ERR(l)) {
207 n = PTR_ERR(l);
208 goto not_found;
209 }
210
211 if (debug_mask & DEBUG_ACCESS)
212 pr_info("wake_unlock_store: %s\n", l->name);
213
214 wake_unlock(&l->wake_lock);
215not_found:
216 mutex_unlock(&tree_lock);
217 return n;
218}
219