|  | /* kernel/power/userwakelock.c | 
|  | * | 
|  | * Copyright (C) 2005-2008 Google, Inc. | 
|  | * | 
|  | * This software is licensed under the terms of the GNU General Public | 
|  | * License version 2, as published by the Free Software Foundation, and | 
|  | * may be copied, distributed, and modified under those terms. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <linux/ctype.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/wakelock.h> | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | #include "power.h" | 
|  |  | 
|  | enum { | 
|  | DEBUG_FAILURE	= BIT(0), | 
|  | DEBUG_ERROR	= BIT(1), | 
|  | DEBUG_NEW	= BIT(2), | 
|  | DEBUG_ACCESS	= BIT(3), | 
|  | DEBUG_LOOKUP	= BIT(4), | 
|  | }; | 
|  | static int debug_mask = DEBUG_FAILURE; | 
|  | module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); | 
|  |  | 
|  | static DEFINE_MUTEX(tree_lock); | 
|  |  | 
|  | struct user_wake_lock { | 
|  | struct rb_node		node; | 
|  | struct wake_lock	wake_lock; | 
|  | char			name[0]; | 
|  | }; | 
|  | struct rb_root user_wake_locks; | 
|  |  | 
|  | static struct user_wake_lock *lookup_wake_lock_name( | 
|  | const char *buf, int allocate, long *timeoutptr) | 
|  | { | 
|  | struct rb_node **p = &user_wake_locks.rb_node; | 
|  | struct rb_node *parent = NULL; | 
|  | struct user_wake_lock *l; | 
|  | int diff; | 
|  | u64 timeout; | 
|  | int name_len; | 
|  | const char *arg; | 
|  |  | 
|  | /* Find length of lock name and start of optional timeout string */ | 
|  | arg = buf; | 
|  | while (*arg && !isspace(*arg)) | 
|  | arg++; | 
|  | name_len = arg - buf; | 
|  | if (!name_len) | 
|  | goto bad_arg; | 
|  | while (isspace(*arg)) | 
|  | arg++; | 
|  |  | 
|  | /* Process timeout string */ | 
|  | if (timeoutptr && *arg) { | 
|  | timeout = simple_strtoull(arg, (char **)&arg, 0); | 
|  | while (isspace(*arg)) | 
|  | arg++; | 
|  | if (*arg) | 
|  | goto bad_arg; | 
|  | /* convert timeout from nanoseconds to jiffies > 0 */ | 
|  | timeout += (NSEC_PER_SEC / HZ) - 1; | 
|  | do_div(timeout, (NSEC_PER_SEC / HZ)); | 
|  | if (timeout <= 0) | 
|  | timeout = 1; | 
|  | *timeoutptr = timeout; | 
|  | } else if (*arg) | 
|  | goto bad_arg; | 
|  | else if (timeoutptr) | 
|  | *timeoutptr = 0; | 
|  |  | 
|  | /* Lookup wake lock in rbtree */ | 
|  | while (*p) { | 
|  | parent = *p; | 
|  | l = rb_entry(parent, struct user_wake_lock, node); | 
|  | diff = strncmp(buf, l->name, name_len); | 
|  | if (!diff && l->name[name_len]) | 
|  | diff = -1; | 
|  | if (debug_mask & DEBUG_ERROR) | 
|  | pr_info("lookup_wake_lock_name: compare %.*s %s %d\n", | 
|  | name_len, buf, l->name, diff); | 
|  |  | 
|  | if (diff < 0) | 
|  | p = &(*p)->rb_left; | 
|  | else if (diff > 0) | 
|  | p = &(*p)->rb_right; | 
|  | else | 
|  | return l; | 
|  | } | 
|  |  | 
|  | /* Allocate and add new wakelock to rbtree */ | 
|  | if (!allocate) { | 
|  | if (debug_mask & DEBUG_ERROR) | 
|  | pr_info("lookup_wake_lock_name: %.*s not found\n", | 
|  | name_len, buf); | 
|  | return ERR_PTR(-EINVAL); | 
|  | } | 
|  | l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL); | 
|  | if (l == NULL) { | 
|  | if (debug_mask & DEBUG_FAILURE) | 
|  | pr_err("lookup_wake_lock_name: failed to allocate " | 
|  | "memory for %.*s\n", name_len, buf); | 
|  | return ERR_PTR(-ENOMEM); | 
|  | } | 
|  | memcpy(l->name, buf, name_len); | 
|  | if (debug_mask & DEBUG_NEW) | 
|  | pr_info("lookup_wake_lock_name: new wake lock %s\n", l->name); | 
|  | wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name); | 
|  | rb_link_node(&l->node, parent, p); | 
|  | rb_insert_color(&l->node, &user_wake_locks); | 
|  | return l; | 
|  |  | 
|  | bad_arg: | 
|  | if (debug_mask & DEBUG_ERROR) | 
|  | pr_info("lookup_wake_lock_name: wake lock, %.*s, bad arg, %s\n", | 
|  | name_len, buf, arg); | 
|  | return ERR_PTR(-EINVAL); | 
|  | } | 
|  |  | 
|  | ssize_t wake_lock_show( | 
|  | struct kobject *kobj, struct kobj_attribute *attr, char *buf) | 
|  | { | 
|  | char *s = buf; | 
|  | char *end = buf + PAGE_SIZE; | 
|  | struct rb_node *n; | 
|  | struct user_wake_lock *l; | 
|  |  | 
|  | mutex_lock(&tree_lock); | 
|  |  | 
|  | for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) { | 
|  | l = rb_entry(n, struct user_wake_lock, node); | 
|  | if (wake_lock_active(&l->wake_lock)) | 
|  | s += scnprintf(s, end - s, "%s ", l->name); | 
|  | } | 
|  | s += scnprintf(s, end - s, "\n"); | 
|  |  | 
|  | mutex_unlock(&tree_lock); | 
|  | return (s - buf); | 
|  | } | 
|  |  | 
|  | ssize_t wake_lock_store( | 
|  | struct kobject *kobj, struct kobj_attribute *attr, | 
|  | const char *buf, size_t n) | 
|  | { | 
|  | long timeout; | 
|  | struct user_wake_lock *l; | 
|  |  | 
|  | mutex_lock(&tree_lock); | 
|  | l = lookup_wake_lock_name(buf, 1, &timeout); | 
|  | if (IS_ERR(l)) { | 
|  | n = PTR_ERR(l); | 
|  | goto bad_name; | 
|  | } | 
|  |  | 
|  | if (debug_mask & DEBUG_ACCESS) | 
|  | pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout); | 
|  |  | 
|  | if (timeout) | 
|  | wake_lock_timeout(&l->wake_lock, timeout); | 
|  | else | 
|  | wake_lock(&l->wake_lock); | 
|  | bad_name: | 
|  | mutex_unlock(&tree_lock); | 
|  | return n; | 
|  | } | 
|  |  | 
|  |  | 
|  | ssize_t wake_unlock_show( | 
|  | struct kobject *kobj, struct kobj_attribute *attr, char *buf) | 
|  | { | 
|  | char *s = buf; | 
|  | char *end = buf + PAGE_SIZE; | 
|  | struct rb_node *n; | 
|  | struct user_wake_lock *l; | 
|  |  | 
|  | mutex_lock(&tree_lock); | 
|  |  | 
|  | for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) { | 
|  | l = rb_entry(n, struct user_wake_lock, node); | 
|  | if (!wake_lock_active(&l->wake_lock)) | 
|  | s += scnprintf(s, end - s, "%s ", l->name); | 
|  | } | 
|  | s += scnprintf(s, end - s, "\n"); | 
|  |  | 
|  | mutex_unlock(&tree_lock); | 
|  | return (s - buf); | 
|  | } | 
|  |  | 
|  | ssize_t wake_unlock_store( | 
|  | struct kobject *kobj, struct kobj_attribute *attr, | 
|  | const char *buf, size_t n) | 
|  | { | 
|  | struct user_wake_lock *l; | 
|  |  | 
|  | mutex_lock(&tree_lock); | 
|  | l = lookup_wake_lock_name(buf, 0, NULL); | 
|  | if (IS_ERR(l)) { | 
|  | n = PTR_ERR(l); | 
|  | goto not_found; | 
|  | } | 
|  |  | 
|  | if (debug_mask & DEBUG_ACCESS) | 
|  | pr_info("wake_unlock_store: %s\n", l->name); | 
|  |  | 
|  | wake_unlock(&l->wake_lock); | 
|  | not_found: | 
|  | mutex_unlock(&tree_lock); | 
|  | return n; | 
|  | } | 
|  |  |