| Arve Hjønnevåg | 47dfb46 | 2008-10-09 21:01:46 -0700 | [diff] [blame] | 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 |  | 
 | 23 | enum { | 
 | 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 | }; | 
 | 30 | static int debug_mask = DEBUG_FAILURE; | 
 | 31 | module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); | 
 | 32 |  | 
 | 33 | static DEFINE_MUTEX(tree_lock); | 
 | 34 |  | 
 | 35 | struct user_wake_lock { | 
 | 36 | 	struct rb_node		node; | 
 | 37 | 	struct wake_lock	wake_lock; | 
 | 38 | 	char			name[0]; | 
 | 39 | }; | 
 | 40 | struct rb_root user_wake_locks; | 
 | 41 |  | 
 | 42 | static 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 |  | 
 | 122 | bad_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 |  | 
 | 129 | ssize_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 |  | 
 | 150 | ssize_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); | 
 | 171 | bad_name: | 
 | 172 | 	mutex_unlock(&tree_lock); | 
 | 173 | 	return n; | 
 | 174 | } | 
 | 175 |  | 
 | 176 |  | 
 | 177 | ssize_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 |  | 
 | 198 | ssize_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); | 
 | 215 | not_found: | 
 | 216 | 	mutex_unlock(&tree_lock); | 
 | 217 | 	return n; | 
 | 218 | } | 
 | 219 |  |