Reading from this file causes the last string successfully
                written to it to be returned.
+
+What:          /sys/power/wake_lock
+Date:          February 2012
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/power/wake_lock file allows user space to create
+               wakeup source objects and activate them on demand (if one of
+               those wakeup sources is active, reads from the
+               /sys/power/wakeup_count file block or return false).  When a
+               string without white space is written to /sys/power/wake_lock,
+               it will be assumed to represent a wakeup source name.  If there
+               is a wakeup source object with that name, it will be activated
+               (unless active already).  Otherwise, a new wakeup source object
+               will be registered, assigned the given name and activated.
+               If a string written to /sys/power/wake_lock contains white
+               space, the part of the string preceding the white space will be
+               regarded as a wakeup source name and handled as descrived above.
+               The other part of the string will be regarded as a timeout (in
+               nanoseconds) such that the wakeup source will be automatically
+               deactivated after it has expired.  The timeout, if present, is
+               set regardless of the current state of the wakeup source object
+               in question.
+
+               Reads from this file return a string consisting of the names of
+               wakeup sources created with the help of it that are active at
+               the moment, separated with spaces.
+
+
+What:          /sys/power/wake_unlock
+Date:          February 2012
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/power/wake_unlock file allows user space to deactivate
+               wakeup sources created with the help of /sys/power/wake_lock.
+               When a string is written to /sys/power/wake_unlock, it will be
+               assumed to represent the name of a wakeup source to deactivate.
+               If a wakeup source object of that name exists and is active at
+               the moment, it will be deactivated.
+
+               Reads from this file return a string consisting of the names of
+               wakeup sources created with the help of /sys/power/wake_lock
+               that are inactive at the moment, separated with spaces.
 
 
 power_attr(autosleep);
 #endif /* CONFIG_PM_AUTOSLEEP */
+
+#ifdef CONFIG_PM_WAKELOCKS
+static ssize_t wake_lock_show(struct kobject *kobj,
+                             struct kobj_attribute *attr,
+                             char *buf)
+{
+       return pm_show_wakelocks(buf, true);
+}
+
+static ssize_t wake_lock_store(struct kobject *kobj,
+                              struct kobj_attribute *attr,
+                              const char *buf, size_t n)
+{
+       int error = pm_wake_lock(buf);
+       return error ? error : n;
+}
+
+power_attr(wake_lock);
+
+static ssize_t wake_unlock_show(struct kobject *kobj,
+                               struct kobj_attribute *attr,
+                               char *buf)
+{
+       return pm_show_wakelocks(buf, false);
+}
+
+static ssize_t wake_unlock_store(struct kobject *kobj,
+                                struct kobj_attribute *attr,
+                                const char *buf, size_t n)
+{
+       int error = pm_wake_unlock(buf);
+       return error ? error : n;
+}
+
+power_attr(wake_unlock);
+
+#endif /* CONFIG_PM_WAKELOCKS */
 #endif /* CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM_TRACE
 #ifdef CONFIG_PM_AUTOSLEEP
        &autosleep_attr.attr,
 #endif
+#ifdef CONFIG_PM_WAKELOCKS
+       &wake_lock_attr.attr,
+       &wake_unlock_attr.attr,
+#endif
 #ifdef CONFIG_PM_DEBUG
        &pm_test_attr.attr,
 #endif
 
--- /dev/null
+/*
+ * kernel/power/wakelock.c
+ *
+ * User space wakeup sources support.
+ *
+ * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * This code is based on the analogous interface allowing user space to
+ * manipulate wakelocks on Android.
+ */
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+
+#define WL_NUMBER_LIMIT        100
+#define WL_GC_COUNT_MAX        100
+#define WL_GC_TIME_SEC 300
+
+static DEFINE_MUTEX(wakelocks_lock);
+
+struct wakelock {
+       char                    *name;
+       struct rb_node          node;
+       struct wakeup_source    ws;
+       struct list_head        lru;
+};
+
+static struct rb_root wakelocks_tree = RB_ROOT;
+static LIST_HEAD(wakelocks_lru_list);
+static unsigned int number_of_wakelocks;
+static unsigned int wakelocks_gc_count;
+
+ssize_t pm_show_wakelocks(char *buf, bool show_active)
+{
+       struct rb_node *node;
+       struct wakelock *wl;
+       char *str = buf;
+       char *end = buf + PAGE_SIZE;
+
+       mutex_lock(&wakelocks_lock);
+
+       for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
+               wl = rb_entry(node, struct wakelock, node);
+               if (wl->ws.active == show_active)
+                       str += scnprintf(str, end - str, "%s ", wl->name);
+       }
+       if (str > buf)
+               str--;
+
+       str += scnprintf(str, end - str, "\n");
+
+       mutex_unlock(&wakelocks_lock);
+       return (str - buf);
+}
+
+static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
+                                           bool add_if_not_found)
+{
+       struct rb_node **node = &wakelocks_tree.rb_node;
+       struct rb_node *parent = *node;
+       struct wakelock *wl;
+
+       while (*node) {
+               int diff;
+
+               parent = *node;
+               wl = rb_entry(*node, struct wakelock, node);
+               diff = strncmp(name, wl->name, len);
+               if (diff == 0) {
+                       if (wl->name[len])
+                               diff = -1;
+                       else
+                               return wl;
+               }
+               if (diff < 0)
+                       node = &(*node)->rb_left;
+               else
+                       node = &(*node)->rb_right;
+       }
+       if (!add_if_not_found)
+               return ERR_PTR(-EINVAL);
+
+       if (number_of_wakelocks > WL_NUMBER_LIMIT)
+               return ERR_PTR(-ENOSPC);
+
+       /* Not found, we have to add a new one. */
+       wl = kzalloc(sizeof(*wl), GFP_KERNEL);
+       if (!wl)
+               return ERR_PTR(-ENOMEM);
+
+       wl->name = kstrndup(name, len, GFP_KERNEL);
+       if (!wl->name) {
+               kfree(wl);
+               return ERR_PTR(-ENOMEM);
+       }
+       wl->ws.name = wl->name;
+       wakeup_source_add(&wl->ws);
+       rb_link_node(&wl->node, parent, node);
+       rb_insert_color(&wl->node, &wakelocks_tree);
+       list_add(&wl->lru, &wakelocks_lru_list);
+       number_of_wakelocks++;
+       return wl;
+}
+
+int pm_wake_lock(const char *buf)
+{
+       const char *str = buf;
+       struct wakelock *wl;
+       u64 timeout_ns = 0;
+       size_t len;
+       int ret = 0;
+
+       while (*str && !isspace(*str))
+               str++;
+
+       len = str - buf;
+       if (!len)
+               return -EINVAL;
+
+       if (*str && *str != '\n') {
+               /* Find out if there's a valid timeout string appended. */
+               ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
+               if (ret)
+                       return -EINVAL;
+       }
+
+       mutex_lock(&wakelocks_lock);
+
+       wl = wakelock_lookup_add(buf, len, true);
+       if (IS_ERR(wl)) {
+               ret = PTR_ERR(wl);
+               goto out;
+       }
+       if (timeout_ns) {
+               u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
+
+               do_div(timeout_ms, NSEC_PER_MSEC);
+               __pm_wakeup_event(&wl->ws, timeout_ms);
+       } else {
+               __pm_stay_awake(&wl->ws);
+       }
+
+       list_move(&wl->lru, &wakelocks_lru_list);
+
+ out:
+       mutex_unlock(&wakelocks_lock);
+       return ret;
+}
+
+static void wakelocks_gc(void)
+{
+       struct wakelock *wl, *aux;
+       ktime_t now = ktime_get();
+
+       list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
+               u64 idle_time_ns;
+               bool active;
+
+               spin_lock_irq(&wl->ws.lock);
+               idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
+               active = wl->ws.active;
+               spin_unlock_irq(&wl->ws.lock);
+
+               if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
+                       break;
+
+               if (!active) {
+                       wakeup_source_remove(&wl->ws);
+                       rb_erase(&wl->node, &wakelocks_tree);
+                       list_del(&wl->lru);
+                       kfree(wl->name);
+                       kfree(wl);
+                       number_of_wakelocks--;
+               }
+       }
+       wakelocks_gc_count = 0;
+}
+
+int pm_wake_unlock(const char *buf)
+{
+       struct wakelock *wl;
+       size_t len;
+       int ret = 0;
+
+       len = strlen(buf);
+       if (!len)
+               return -EINVAL;
+
+       if (buf[len-1] == '\n')
+               len--;
+
+       if (!len)
+               return -EINVAL;
+
+       mutex_lock(&wakelocks_lock);
+
+       wl = wakelock_lookup_add(buf, len, false);
+       if (IS_ERR(wl)) {
+               ret = PTR_ERR(wl);
+               goto out;
+       }
+       __pm_relax(&wl->ws);
+       list_move(&wl->lru, &wakelocks_lru_list);
+       if (++wakelocks_gc_count > WL_GC_COUNT_MAX)
+               wakelocks_gc();
+
+ out:
+       mutex_unlock(&wakelocks_lock);
+       return ret;
+}