aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/power')
-rw-r--r--kernel/power/consoleearlysuspend.c78
-rw-r--r--kernel/power/earlysuspend.c187
-rw-r--r--kernel/power/fbearlysuspend.c153
-rw-r--r--kernel/power/suspend_time.c111
-rw-r--r--kernel/power/userwakelock.c219
5 files changed, 748 insertions, 0 deletions
diff --git a/kernel/power/consoleearlysuspend.c b/kernel/power/consoleearlysuspend.c
new file mode 100644
index 00000000000..a3edcb26738
--- /dev/null
+++ b/kernel/power/consoleearlysuspend.c
@@ -0,0 +1,78 @@
1/* kernel/power/consoleearlysuspend.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/console.h>
17#include <linux/earlysuspend.h>
18#include <linux/kbd_kern.h>
19#include <linux/module.h>
20#include <linux/vt_kern.h>
21#include <linux/wait.h>
22
23#define EARLY_SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
24
25static int orig_fgconsole;
26static void console_early_suspend(struct early_suspend *h)
27{
28 acquire_console_sem();
29 orig_fgconsole = fg_console;
30 if (vc_allocate(EARLY_SUSPEND_CONSOLE))
31 goto err;
32 if (set_console(EARLY_SUSPEND_CONSOLE))
33 goto err;
34 release_console_sem();
35
36 if (vt_waitactive(EARLY_SUSPEND_CONSOLE + 1))
37 pr_warning("console_early_suspend: Can't switch VCs.\n");
38 return;
39err:
40 pr_warning("console_early_suspend: Can't set console\n");
41 release_console_sem();
42}
43
44static void console_late_resume(struct early_suspend *h)
45{
46 int ret;
47 acquire_console_sem();
48 ret = set_console(orig_fgconsole);
49 release_console_sem();
50 if (ret) {
51 pr_warning("console_late_resume: Can't set console.\n");
52 return;
53 }
54
55 if (vt_waitactive(orig_fgconsole + 1))
56 pr_warning("console_late_resume: Can't switch VCs.\n");
57}
58
59static struct early_suspend console_early_suspend_desc = {
60 .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
61 .suspend = console_early_suspend,
62 .resume = console_late_resume,
63};
64
65static int __init console_early_suspend_init(void)
66{
67 register_early_suspend(&console_early_suspend_desc);
68 return 0;
69}
70
71static void __exit console_early_suspend_exit(void)
72{
73 unregister_early_suspend(&console_early_suspend_desc);
74}
75
76module_init(console_early_suspend_init);
77module_exit(console_early_suspend_exit);
78
diff --git a/kernel/power/earlysuspend.c b/kernel/power/earlysuspend.c
new file mode 100644
index 00000000000..b15f02eba45
--- /dev/null
+++ b/kernel/power/earlysuspend.c
@@ -0,0 +1,187 @@
1/* kernel/power/earlysuspend.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/earlysuspend.h>
17#include <linux/module.h>
18#include <linux/mutex.h>
19#include <linux/rtc.h>
20#include <linux/syscalls.h> /* sys_sync */
21#include <linux/wakelock.h>
22#include <linux/workqueue.h>
23
24#include "power.h"
25
26enum {
27 DEBUG_USER_STATE = 1U << 0,
28 DEBUG_SUSPEND = 1U << 2,
29 DEBUG_VERBOSE = 1U << 3,
30};
31static int debug_mask = DEBUG_USER_STATE;
32module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
33
34static DEFINE_MUTEX(early_suspend_lock);
35static LIST_HEAD(early_suspend_handlers);
36static void early_suspend(struct work_struct *work);
37static void late_resume(struct work_struct *work);
38static DECLARE_WORK(early_suspend_work, early_suspend);
39static DECLARE_WORK(late_resume_work, late_resume);
40static DEFINE_SPINLOCK(state_lock);
41enum {
42 SUSPEND_REQUESTED = 0x1,
43 SUSPENDED = 0x2,
44 SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
45};
46static int state;
47
48void register_early_suspend(struct early_suspend *handler)
49{
50 struct list_head *pos;
51
52 mutex_lock(&early_suspend_lock);
53 list_for_each(pos, &early_suspend_handlers) {
54 struct early_suspend *e;
55 e = list_entry(pos, struct early_suspend, link);
56 if (e->level > handler->level)
57 break;
58 }
59 list_add_tail(&handler->link, pos);
60 if ((state & SUSPENDED) && handler->suspend)
61 handler->suspend(handler);
62 mutex_unlock(&early_suspend_lock);
63}
64EXPORT_SYMBOL(register_early_suspend);
65
66void unregister_early_suspend(struct early_suspend *handler)
67{
68 mutex_lock(&early_suspend_lock);
69 list_del(&handler->link);
70 mutex_unlock(&early_suspend_lock);
71}
72EXPORT_SYMBOL(unregister_early_suspend);
73
74static void early_suspend(struct work_struct *work)
75{
76 struct early_suspend *pos;
77 unsigned long irqflags;
78 int abort = 0;
79
80 mutex_lock(&early_suspend_lock);
81 spin_lock_irqsave(&state_lock, irqflags);
82 if (state == SUSPEND_REQUESTED)
83 state |= SUSPENDED;
84 else
85 abort = 1;
86 spin_unlock_irqrestore(&state_lock, irqflags);
87
88 if (abort) {
89 if (debug_mask & DEBUG_SUSPEND)
90 pr_info("early_suspend: abort, state %d\n", state);
91 mutex_unlock(&early_suspend_lock);
92 goto abort;
93 }
94
95 if (debug_mask & DEBUG_SUSPEND)
96 pr_info("early_suspend: call handlers\n");
97 list_for_each_entry(pos, &early_suspend_handlers, link) {
98 if (pos->suspend != NULL) {
99 if (debug_mask & DEBUG_VERBOSE)
100 pr_info("early_suspend: calling %pf\n", pos->suspend);
101 pos->suspend(pos);
102 }
103 }
104 mutex_unlock(&early_suspend_lock);
105
106 if (debug_mask & DEBUG_SUSPEND)
107 pr_info("early_suspend: sync\n");
108
109 sys_sync();
110abort:
111 spin_lock_irqsave(&state_lock, irqflags);
112 if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
113 wake_unlock(&main_wake_lock);
114 spin_unlock_irqrestore(&state_lock, irqflags);
115}
116
117static void late_resume(struct work_struct *work)
118{
119 struct early_suspend *pos;
120 unsigned long irqflags;
121 int abort = 0;
122
123 mutex_lock(&early_suspend_lock);
124 spin_lock_irqsave(&state_lock, irqflags);
125 if (state == SUSPENDED)
126 state &= ~SUSPENDED;
127 else
128 abort = 1;
129 spin_unlock_irqrestore(&state_lock, irqflags);
130
131 if (abort) {
132 if (debug_mask & DEBUG_SUSPEND)
133 pr_info("late_resume: abort, state %d\n", state);
134 goto abort;
135 }
136 if (debug_mask & DEBUG_SUSPEND)
137 pr_info("late_resume: call handlers\n");
138 list_for_each_entry_reverse(pos, &early_suspend_handlers, link) {
139 if (pos->resume != NULL) {
140 if (debug_mask & DEBUG_VERBOSE)
141 pr_info("late_resume: calling %pf\n", pos->resume);
142
143 pos->resume(pos);
144 }
145 }
146 if (debug_mask & DEBUG_SUSPEND)
147 pr_info("late_resume: done\n");
148abort:
149 mutex_unlock(&early_suspend_lock);
150}
151
152void request_suspend_state(suspend_state_t new_state)
153{
154 unsigned long irqflags;
155 int old_sleep;
156
157 spin_lock_irqsave(&state_lock, irqflags);
158 old_sleep = state & SUSPEND_REQUESTED;
159 if (debug_mask & DEBUG_USER_STATE) {
160 struct timespec ts;
161 struct rtc_time tm;
162 getnstimeofday(&ts);
163 rtc_time_to_tm(ts.tv_sec, &tm);
164 pr_info("request_suspend_state: %s (%d->%d) at %lld "
165 "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
166 new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
167 requested_suspend_state, new_state,
168 ktime_to_ns(ktime_get()),
169 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
170 tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
171 }
172 if (!old_sleep && new_state != PM_SUSPEND_ON) {
173 state |= SUSPEND_REQUESTED;
174 queue_work(suspend_work_queue, &early_suspend_work);
175 } else if (old_sleep && new_state == PM_SUSPEND_ON) {
176 state &= ~SUSPEND_REQUESTED;
177 wake_lock(&main_wake_lock);
178 queue_work(suspend_work_queue, &late_resume_work);
179 }
180 requested_suspend_state = new_state;
181 spin_unlock_irqrestore(&state_lock, irqflags);
182}
183
184suspend_state_t get_suspend_state(void)
185{
186 return requested_suspend_state;
187}
diff --git a/kernel/power/fbearlysuspend.c b/kernel/power/fbearlysuspend.c
new file mode 100644
index 00000000000..15137650149
--- /dev/null
+++ b/kernel/power/fbearlysuspend.c
@@ -0,0 +1,153 @@
1/* kernel/power/fbearlysuspend.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/earlysuspend.h>
17#include <linux/module.h>
18#include <linux/wait.h>
19
20#include "power.h"
21
22static wait_queue_head_t fb_state_wq;
23static DEFINE_SPINLOCK(fb_state_lock);
24static enum {
25 FB_STATE_STOPPED_DRAWING,
26 FB_STATE_REQUEST_STOP_DRAWING,
27 FB_STATE_DRAWING_OK,
28} fb_state;
29
30/* tell userspace to stop drawing, wait for it to stop */
31static void stop_drawing_early_suspend(struct early_suspend *h)
32{
33 int ret;
34 unsigned long irq_flags;
35
36 spin_lock_irqsave(&fb_state_lock, irq_flags);
37 fb_state = FB_STATE_REQUEST_STOP_DRAWING;
38 spin_unlock_irqrestore(&fb_state_lock, irq_flags);
39
40 wake_up_all(&fb_state_wq);
41 ret = wait_event_timeout(fb_state_wq,
42 fb_state == FB_STATE_STOPPED_DRAWING,
43 HZ);
44 if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING))
45 pr_warning("stop_drawing_early_suspend: timeout waiting for "
46 "userspace to stop drawing\n");
47}
48
49/* tell userspace to start drawing */
50static void start_drawing_late_resume(struct early_suspend *h)
51{
52 unsigned long irq_flags;
53
54 spin_lock_irqsave(&fb_state_lock, irq_flags);
55 fb_state = FB_STATE_DRAWING_OK;
56 spin_unlock_irqrestore(&fb_state_lock, irq_flags);
57 wake_up(&fb_state_wq);
58}
59
60static struct early_suspend stop_drawing_early_suspend_desc = {
61 .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
62 .suspend = stop_drawing_early_suspend,
63 .resume = start_drawing_late_resume,
64};
65
66static ssize_t wait_for_fb_sleep_show(struct kobject *kobj,
67 struct kobj_attribute *attr, char *buf)
68{
69 char *s = buf;
70 int ret;
71
72 ret = wait_event_interruptible(fb_state_wq,
73 fb_state != FB_STATE_DRAWING_OK);
74 if (ret && fb_state == FB_STATE_DRAWING_OK)
75 return ret;
76 else
77 s += sprintf(buf, "sleeping");
78 return s - buf;
79}
80
81static ssize_t wait_for_fb_wake_show(struct kobject *kobj,
82 struct kobj_attribute *attr, char *buf)
83{
84 char *s = buf;
85 int ret;
86 unsigned long irq_flags;
87
88 spin_lock_irqsave(&fb_state_lock, irq_flags);
89 if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) {
90 fb_state = FB_STATE_STOPPED_DRAWING;
91 wake_up(&fb_state_wq);
92 }
93 spin_unlock_irqrestore(&fb_state_lock, irq_flags);
94
95 ret = wait_event_interruptible(fb_state_wq,
96 fb_state == FB_STATE_DRAWING_OK);
97 if (ret && fb_state != FB_STATE_DRAWING_OK)
98 return ret;
99 else
100 s += sprintf(buf, "awake");
101
102 return s - buf;
103}
104
105#define power_ro_attr(_name) \
106static struct kobj_attribute _name##_attr = { \
107 .attr = { \
108 .name = __stringify(_name), \
109 .mode = 0444, \
110 }, \
111 .show = _name##_show, \
112 .store = NULL, \
113}
114
115power_ro_attr(wait_for_fb_sleep);
116power_ro_attr(wait_for_fb_wake);
117
118static struct attribute *g[] = {
119 &wait_for_fb_sleep_attr.attr,
120 &wait_for_fb_wake_attr.attr,
121 NULL,
122};
123
124static struct attribute_group attr_group = {
125 .attrs = g,
126};
127
128static int __init android_power_init(void)
129{
130 int ret;
131
132 init_waitqueue_head(&fb_state_wq);
133 fb_state = FB_STATE_DRAWING_OK;
134
135 ret = sysfs_create_group(power_kobj, &attr_group);
136 if (ret) {
137 pr_err("android_power_init: sysfs_create_group failed\n");
138 return ret;
139 }
140
141 register_early_suspend(&stop_drawing_early_suspend_desc);
142 return 0;
143}
144
145static void __exit android_power_exit(void)
146{
147 unregister_early_suspend(&stop_drawing_early_suspend_desc);
148 sysfs_remove_group(power_kobj, &attr_group);
149}
150
151module_init(android_power_init);
152module_exit(android_power_exit);
153
diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c
new file mode 100644
index 00000000000..d2a65da9f22
--- /dev/null
+++ b/kernel/power/suspend_time.c
@@ -0,0 +1,111 @@
1/*
2 * debugfs file to track time spent in suspend
3 *
4 * Copyright (c) 2011, Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 */
16
17#include <linux/debugfs.h>
18#include <linux/err.h>
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/seq_file.h>
22#include <linux/syscore_ops.h>
23#include <linux/time.h>
24
25static struct timespec suspend_time_before;
26static unsigned int time_in_suspend_bins[32];
27
28#ifdef CONFIG_DEBUG_FS
29static int suspend_time_debug_show(struct seq_file *s, void *data)
30{
31 int bin;
32 seq_printf(s, "time (secs) count\n");
33 seq_printf(s, "------------------\n");
34 for (bin = 0; bin < 32; bin++) {
35 if (time_in_suspend_bins[bin] == 0)
36 continue;
37 seq_printf(s, "%4d - %4d %4u\n",
38 bin ? 1 << (bin - 1) : 0, 1 << bin,
39 time_in_suspend_bins[bin]);
40 }
41 return 0;
42}
43
44static int suspend_time_debug_open(struct inode *inode, struct file *file)
45{
46 return single_open(file, suspend_time_debug_show, NULL);
47}
48
49static const struct file_operations suspend_time_debug_fops = {
50 .open = suspend_time_debug_open,
51 .read = seq_read,
52 .llseek = seq_lseek,
53 .release = single_release,
54};
55
56static int __init suspend_time_debug_init(void)
57{
58 struct dentry *d;
59
60 d = debugfs_create_file("suspend_time", 0755, NULL, NULL,
61 &suspend_time_debug_fops);
62 if (!d) {
63 pr_err("Failed to create suspend_time debug file\n");
64 return -ENOMEM;
65 }
66
67 return 0;
68}
69
70late_initcall(suspend_time_debug_init);
71#endif
72
73static int suspend_time_syscore_suspend(void)
74{
75 read_persistent_clock(&suspend_time_before);
76
77 return 0;
78}
79
80static void suspend_time_syscore_resume(void)
81{
82 struct timespec after;
83
84 read_persistent_clock(&after);
85
86 after = timespec_sub(after, suspend_time_before);
87
88 time_in_suspend_bins[fls(after.tv_sec)]++;
89
90 pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec,
91 after.tv_nsec / NSEC_PER_MSEC);
92}
93
94static struct syscore_ops suspend_time_syscore_ops = {
95 .suspend = suspend_time_syscore_suspend,
96 .resume = suspend_time_syscore_resume,
97};
98
99static int suspend_time_syscore_init(void)
100{
101 register_syscore_ops(&suspend_time_syscore_ops);
102
103 return 0;
104}
105
106static void suspend_time_syscore_exit(void)
107{
108 unregister_syscore_ops(&suspend_time_syscore_ops);
109}
110module_init(suspend_time_syscore_init);
111module_exit(suspend_time_syscore_exit);
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