• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 #include "power.h"
21 
22 enum {
23 	DEBUG_FAILURE	= BIT(0),
24 	DEBUG_ERROR	= BIT(1),
25 	DEBUG_NEW	= BIT(2),
26 	DEBUG_ACCESS	= BIT(3),
27 	DEBUG_LOOKUP	= BIT(4),
28 };
29 static int debug_mask = DEBUG_FAILURE;
30 module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
31 
32 static DEFINE_MUTEX(tree_lock);
33 
34 struct user_wake_lock {
35 	struct rb_node		node;
36 	struct wake_lock	wake_lock;
37 	char			name[0];
38 };
39 struct rb_root user_wake_locks;
40 
lookup_wake_lock_name(const char * buf,int allocate,long * timeoutptr)41 static struct user_wake_lock *lookup_wake_lock_name(
42 	const char *buf, int allocate, long *timeoutptr)
43 {
44 	struct rb_node **p = &user_wake_locks.rb_node;
45 	struct rb_node *parent = NULL;
46 	struct user_wake_lock *l;
47 	int diff;
48 	u64 timeout;
49 	int name_len;
50 	const char *arg;
51 
52 	/* Find length of lock name and start of optional timeout string */
53 	arg = buf;
54 	while (*arg && !isspace(*arg))
55 		arg++;
56 	name_len = arg - buf;
57 	if (!name_len)
58 		goto bad_arg;
59 	while (isspace(*arg))
60 		arg++;
61 
62 	/* Process timeout string */
63 	if (timeoutptr && *arg) {
64 		timeout = simple_strtoull(arg, (char **)&arg, 0);
65 		while (isspace(*arg))
66 			arg++;
67 		if (*arg)
68 			goto bad_arg;
69 		/* convert timeout from nanoseconds to jiffies > 0 */
70 		timeout += (NSEC_PER_SEC / HZ) - 1;
71 		do_div(timeout, (NSEC_PER_SEC / HZ));
72 		if (timeout <= 0)
73 			timeout = 1;
74 		*timeoutptr = timeout;
75 	} else if (*arg)
76 		goto bad_arg;
77 	else if (timeoutptr)
78 		*timeoutptr = 0;
79 
80 	/* Lookup wake lock in rbtree */
81 	while (*p) {
82 		parent = *p;
83 		l = rb_entry(parent, struct user_wake_lock, node);
84 		diff = strncmp(buf, l->name, name_len);
85 		if (!diff && l->name[name_len])
86 			diff = -1;
87 		if (debug_mask & DEBUG_ERROR)
88 			pr_info("lookup_wake_lock_name: compare %.*s %s %d\n",
89 				name_len, buf, l->name, diff);
90 
91 		if (diff < 0)
92 			p = &(*p)->rb_left;
93 		else if (diff > 0)
94 			p = &(*p)->rb_right;
95 		else
96 			return l;
97 	}
98 
99 	/* Allocate and add new wakelock to rbtree */
100 	if (!allocate) {
101 		if (debug_mask & DEBUG_ERROR)
102 			pr_info("lookup_wake_lock_name: %.*s not found\n",
103 				name_len, buf);
104 		return ERR_PTR(-EINVAL);
105 	}
106 	l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL);
107 	if (l == NULL) {
108 		if (debug_mask & DEBUG_FAILURE)
109 			pr_err("lookup_wake_lock_name: failed to allocate "
110 				"memory for %.*s\n", name_len, buf);
111 		return ERR_PTR(-ENOMEM);
112 	}
113 	memcpy(l->name, buf, name_len);
114 	if (debug_mask & DEBUG_NEW)
115 		pr_info("lookup_wake_lock_name: new wake lock %s\n", l->name);
116 	wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name);
117 	rb_link_node(&l->node, parent, p);
118 	rb_insert_color(&l->node, &user_wake_locks);
119 	return l;
120 
121 bad_arg:
122 	if (debug_mask & DEBUG_ERROR)
123 		pr_info("lookup_wake_lock_name: wake lock, %.*s, bad arg, %s\n",
124 			name_len, buf, arg);
125 	return ERR_PTR(-EINVAL);
126 }
127 
wake_lock_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)128 ssize_t wake_lock_show(
129 	struct kobject *kobj, struct kobj_attribute *attr, char *buf)
130 {
131 	char *s = buf;
132 	char *end = buf + PAGE_SIZE;
133 	struct rb_node *n;
134 	struct user_wake_lock *l;
135 
136 	mutex_lock(&tree_lock);
137 
138 	for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
139 		l = rb_entry(n, struct user_wake_lock, node);
140 		if (wake_lock_active(&l->wake_lock))
141 			s += scnprintf(s, end - s, "%s ", l->name);
142 	}
143 	s += scnprintf(s, end - s, "\n");
144 
145 	mutex_unlock(&tree_lock);
146 	return (s - buf);
147 }
148 
wake_lock_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)149 ssize_t wake_lock_store(
150 	struct kobject *kobj, struct kobj_attribute *attr,
151 	const char *buf, size_t n)
152 {
153 	long timeout;
154 	struct user_wake_lock *l;
155 
156 	mutex_lock(&tree_lock);
157 	l = lookup_wake_lock_name(buf, 1, &timeout);
158 	if (IS_ERR(l)) {
159 		n = PTR_ERR(l);
160 		goto bad_name;
161 	}
162 
163 	if (debug_mask & DEBUG_ACCESS)
164 		pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);
165 
166 	if (timeout)
167 		wake_lock_timeout(&l->wake_lock, timeout);
168 	else
169 		wake_lock(&l->wake_lock);
170 bad_name:
171 	mutex_unlock(&tree_lock);
172 	return n;
173 }
174 
175 
wake_unlock_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)176 ssize_t wake_unlock_show(
177 	struct kobject *kobj, struct kobj_attribute *attr, char *buf)
178 {
179 	char *s = buf;
180 	char *end = buf + PAGE_SIZE;
181 	struct rb_node *n;
182 	struct user_wake_lock *l;
183 
184 	mutex_lock(&tree_lock);
185 
186 	for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
187 		l = rb_entry(n, struct user_wake_lock, node);
188 		if (!wake_lock_active(&l->wake_lock))
189 			s += scnprintf(s, end - s, "%s ", l->name);
190 	}
191 	s += scnprintf(s, end - s, "\n");
192 
193 	mutex_unlock(&tree_lock);
194 	return (s - buf);
195 }
196 
wake_unlock_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)197 ssize_t wake_unlock_store(
198 	struct kobject *kobj, struct kobj_attribute *attr,
199 	const char *buf, size_t n)
200 {
201 	struct user_wake_lock *l;
202 
203 	mutex_lock(&tree_lock);
204 	l = lookup_wake_lock_name(buf, 0, NULL);
205 	if (IS_ERR(l)) {
206 		n = PTR_ERR(l);
207 		goto not_found;
208 	}
209 
210 	if (debug_mask & DEBUG_ACCESS)
211 		pr_info("wake_unlock_store: %s\n", l->name);
212 
213 	wake_unlock(&l->wake_lock);
214 not_found:
215 	mutex_unlock(&tree_lock);
216 	return n;
217 }
218 
219