ps3: R.I.P.
[openwrt.git] / target / linux / goldfish / patches-2.6.30 / 0069-PM-Add-user-space-wake-lock-api.patch
1 From 48e1af2bdd11204f11b3770a6c8d3eee64aee2e8 Mon Sep 17 00:00:00 2001
2 From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
3 Date: Thu, 9 Oct 2008 21:01:46 -0700
4 Subject: [PATCH 069/134] PM: Add user-space wake lock api.
5
6 This adds /sys/power/wake_lock and /sys/power/wake_unlock.
7 Writing a string to wake_lock creates a wake lock the
8 first time is sees a string and locks it. Optionally, the
9 string can be followed by a timeout.
10 To unlock the wake lock, write the same string to wake_unlock.
11 ---
12  kernel/power/Kconfig        |   10 ++
13  kernel/power/Makefile       |    1 +
14  kernel/power/main.c         |    9 ++
15  kernel/power/power.h        |   11 ++
16  kernel/power/userwakelock.c |  218 +++++++++++++++++++++++++++++++++++++++++++
17  5 files changed, 249 insertions(+), 0 deletions(-)
18  create mode 100644 kernel/power/userwakelock.c
19
20 --- a/kernel/power/Kconfig
21 +++ b/kernel/power/Kconfig
22 @@ -138,6 +138,16 @@ config WAKELOCK_STAT
23         ---help---
24           Report wake lock stats in /proc/wakelocks
25  
26 +config USER_WAKELOCK
27 +       bool "Userspace wake locks"
28 +       depends on WAKELOCK
29 +       default y
30 +       ---help---
31 +         User-space wake lock api. Write "lockname" or "lockname timeout"
32 +         to /sys/power/wake_lock lock and if needed create a wake lock.
33 +         Write "lockname" to /sys/power/wake_unlock to unlock a user wake
34 +         lock.
35 +
36  config EARLYSUSPEND
37         bool "Early suspend"
38         depends on WAKELOCK
39 --- a/kernel/power/Makefile
40 +++ b/kernel/power/Makefile
41 @@ -7,6 +7,7 @@ obj-$(CONFIG_PM)                += main.o
42  obj-$(CONFIG_PM_SLEEP)         += console.o
43  obj-$(CONFIG_FREEZER)          += process.o
44  obj-$(CONFIG_WAKELOCK)         += wakelock.o
45 +obj-$(CONFIG_USER_WAKELOCK)    += userwakelock.o
46  obj-$(CONFIG_EARLYSUSPEND)     += earlysuspend.o
47  obj-$(CONFIG_HIBERNATION)      += swsusp.o disk.o snapshot.o swap.o user.o
48  
49 --- a/kernel/power/main.c
50 +++ b/kernel/power/main.c
51 @@ -595,6 +595,11 @@ pm_trace_store(struct kobject *kobj, str
52  power_attr(pm_trace);
53  #endif /* CONFIG_PM_TRACE */
54  
55 +#ifdef CONFIG_USER_WAKELOCK
56 +power_attr(wake_lock);
57 +power_attr(wake_unlock);
58 +#endif
59 +
60  static struct attribute * g[] = {
61         &state_attr.attr,
62  #ifdef CONFIG_PM_TRACE
63 @@ -603,6 +608,10 @@ static struct attribute * g[] = {
64  #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
65         &pm_test_attr.attr,
66  #endif
67 +#ifdef CONFIG_USER_WAKELOCK
68 +       &wake_lock_attr.attr,
69 +       &wake_unlock_attr.attr,
70 +#endif
71         NULL,
72  };
73  
74 --- a/kernel/power/power.h
75 +++ b/kernel/power/power.h
76 @@ -231,6 +231,17 @@ extern struct wake_lock main_wake_lock;
77  extern suspend_state_t requested_suspend_state;
78  #endif
79  
80 +#ifdef CONFIG_USER_WAKELOCK
81 +ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr,
82 +                       char *buf);
83 +ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr,
84 +                       const char *buf, size_t n);
85 +ssize_t wake_unlock_show(struct kobject *kobj, struct kobj_attribute *attr,
86 +                       char *buf);
87 +ssize_t  wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr,
88 +                       const char *buf, size_t n);
89 +#endif
90 +
91  #ifdef CONFIG_EARLYSUSPEND
92  /* kernel/power/earlysuspend.c */
93  void request_suspend_state(suspend_state_t state);
94 --- /dev/null
95 +++ b/kernel/power/userwakelock.c
96 @@ -0,0 +1,218 @@
97 +/* kernel/power/userwakelock.c
98 + *
99 + * Copyright (C) 2005-2008 Google, Inc.
100 + *
101 + * This software is licensed under the terms of the GNU General Public
102 + * License version 2, as published by the Free Software Foundation, and
103 + * may be copied, distributed, and modified under those terms.
104 + *
105 + * This program is distributed in the hope that it will be useful,
106 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
107 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
108 + * GNU General Public License for more details.
109 + *
110 + */
111 +
112 +#include <linux/ctype.h>
113 +#include <linux/module.h>
114 +#include <linux/wakelock.h>
115 +
116 +#include "power.h"
117 +
118 +enum {
119 +       DEBUG_FAILURE   = BIT(0),
120 +       DEBUG_ERROR     = BIT(1),
121 +       DEBUG_NEW       = BIT(2),
122 +       DEBUG_ACCESS    = BIT(3),
123 +       DEBUG_LOOKUP    = BIT(4),
124 +};
125 +static int debug_mask = DEBUG_FAILURE;
126 +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
127 +
128 +static DEFINE_MUTEX(tree_lock);
129 +
130 +struct user_wake_lock {
131 +       struct rb_node          node;
132 +       struct wake_lock        wake_lock;
133 +       char                    name[0];
134 +};
135 +struct rb_root user_wake_locks;
136 +
137 +static struct user_wake_lock *lookup_wake_lock_name(
138 +       const char *buf, int allocate, long *timeoutptr)
139 +{
140 +       struct rb_node **p = &user_wake_locks.rb_node;
141 +       struct rb_node *parent = NULL;
142 +       struct user_wake_lock *l;
143 +       int diff;
144 +       u64 timeout;
145 +       int name_len;
146 +       const char *arg;
147 +
148 +       /* Find length of lock name and start of optional timeout string */
149 +       arg = buf;
150 +       while (*arg && !isspace(*arg))
151 +               arg++;
152 +       name_len = arg - buf;
153 +       if (!name_len)
154 +               goto bad_arg;
155 +       while (isspace(*arg))
156 +               arg++;
157 +
158 +       /* Process timeout string */
159 +       if (timeoutptr && *arg) {
160 +               timeout = simple_strtoull(arg, (char **)&arg, 0);
161 +               while (isspace(*arg))
162 +                       arg++;
163 +               if (*arg)
164 +                       goto bad_arg;
165 +               /* convert timeout from nanoseconds to jiffies > 0 */
166 +               timeout += (NSEC_PER_SEC / HZ) - 1;
167 +               do_div(timeout, (NSEC_PER_SEC / HZ));
168 +               if (timeout <= 0)
169 +                       timeout = 1;
170 +               *timeoutptr = timeout;
171 +       } else if (*arg)
172 +               goto bad_arg;
173 +       else if (timeoutptr)
174 +               *timeoutptr = 0;
175 +
176 +       /* Lookup wake lock in rbtree */
177 +       while (*p) {
178 +               parent = *p;
179 +               l = rb_entry(parent, struct user_wake_lock, node);
180 +               diff = strncmp(buf, l->name, name_len);
181 +               if (!diff && l->name[name_len])
182 +                       diff = -1;
183 +               if (debug_mask & DEBUG_ERROR)
184 +                       pr_info("lookup_wake_lock_name: compare %.*s %s %d\n",
185 +                               name_len, buf, l->name, diff);
186 +
187 +               if (diff < 0)
188 +                       p = &(*p)->rb_left;
189 +               else if (diff > 0)
190 +                       p = &(*p)->rb_right;
191 +               else
192 +                       return l;
193 +       }
194 +
195 +       /* Allocate and add new wakelock to rbtree */
196 +       if (!allocate) {
197 +               if (debug_mask & DEBUG_ERROR)
198 +                       pr_info("lookup_wake_lock_name: %.*s not found\n",
199 +                               name_len, buf);
200 +               return ERR_PTR(-EINVAL);
201 +       }
202 +       l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL);
203 +       if (l == NULL) {
204 +               if (debug_mask & DEBUG_FAILURE)
205 +                       pr_err("lookup_wake_lock_name: failed to allocate "
206 +                               "memory for %.*s\n", name_len, buf);
207 +               return ERR_PTR(-ENOMEM);
208 +       }
209 +       memcpy(l->name, buf, name_len);
210 +       if (debug_mask & DEBUG_NEW)
211 +               pr_info("lookup_wake_lock_name: new wake lock %s\n", l->name);
212 +       wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name);
213 +       rb_link_node(&l->node, parent, p);
214 +       rb_insert_color(&l->node, &user_wake_locks);
215 +       return l;
216 +
217 +bad_arg:
218 +       if (debug_mask & DEBUG_ERROR)
219 +               pr_info("lookup_wake_lock_name: wake lock, %.*s, bad arg, %s\n",
220 +                       name_len, buf, arg);
221 +       return ERR_PTR(-EINVAL);
222 +}
223 +
224 +ssize_t wake_lock_show(
225 +       struct kobject *kobj, struct kobj_attribute *attr, char *buf)
226 +{
227 +       char *s = buf;
228 +       char *end = buf + PAGE_SIZE;
229 +       struct rb_node *n;
230 +       struct user_wake_lock *l;
231 +
232 +       mutex_lock(&tree_lock);
233 +
234 +       for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
235 +               l = rb_entry(n, struct user_wake_lock, node);
236 +               if (wake_lock_active(&l->wake_lock))
237 +                       s += scnprintf(s, end - s, "%s ", l->name);
238 +       }
239 +       s += scnprintf(s, end - s, "\n");
240 +
241 +       mutex_unlock(&tree_lock);
242 +       return (s - buf);
243 +}
244 +
245 +ssize_t wake_lock_store(
246 +       struct kobject *kobj, struct kobj_attribute *attr,
247 +       const char *buf, size_t n)
248 +{
249 +       long timeout;
250 +       struct user_wake_lock *l;
251 +
252 +       mutex_lock(&tree_lock);
253 +       l = lookup_wake_lock_name(buf, 1, &timeout);
254 +       if (IS_ERR(l)) {
255 +               n = PTR_ERR(l);
256 +               goto bad_name;
257 +       }
258 +
259 +       if (debug_mask & DEBUG_ACCESS)
260 +               pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);
261 +
262 +       if (timeout)
263 +               wake_lock_timeout(&l->wake_lock, timeout);
264 +       else
265 +               wake_lock(&l->wake_lock);
266 +bad_name:
267 +       mutex_unlock(&tree_lock);
268 +       return n;
269 +}
270 +
271 +
272 +ssize_t wake_unlock_show(
273 +       struct kobject *kobj, struct kobj_attribute *attr, char *buf)
274 +{
275 +       char *s = buf;
276 +       char *end = buf + PAGE_SIZE;
277 +       struct rb_node *n;
278 +       struct user_wake_lock *l;
279 +
280 +       mutex_lock(&tree_lock);
281 +
282 +       for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
283 +               l = rb_entry(n, struct user_wake_lock, node);
284 +               if (!wake_lock_active(&l->wake_lock))
285 +                       s += scnprintf(s, end - s, "%s ", l->name);
286 +       }
287 +       s += scnprintf(s, end - s, "\n");
288 +
289 +       mutex_unlock(&tree_lock);
290 +       return (s - buf);
291 +}
292 +
293 +ssize_t wake_unlock_store(
294 +       struct kobject *kobj, struct kobj_attribute *attr,
295 +       const char *buf, size_t n)
296 +{
297 +       struct user_wake_lock *l;
298 +
299 +       mutex_lock(&tree_lock);
300 +       l = lookup_wake_lock_name(buf, 0, NULL);
301 +       if (IS_ERR(l)) {
302 +               n = PTR_ERR(l);
303 +               goto not_found;
304 +       }
305 +
306 +       if (debug_mask & DEBUG_ACCESS)
307 +               pr_info("wake_unlock_store: %s\n", l->name);
308 +
309 +       wake_unlock(&l->wake_lock);
310 +not_found:
311 +       mutex_unlock(&tree_lock);
312 +       return n;
313 +}
314 +