aboutsummaryrefslogtreecommitdiff
path: root/drivers/android/power.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/android/power.c')
-rw-r--r--drivers/android/power.c1336
1 files changed, 1336 insertions, 0 deletions
diff --git a/drivers/android/power.c b/drivers/android/power.c
new file mode 100644
index 00000000000..6c773ab1cde
--- /dev/null
+++ b/drivers/android/power.c
@@ -0,0 +1,1336 @@
+/* drivers/android/power.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/list.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+//#include <linux/platform_device.h>
+#include <linux/sysdev.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/rtc.h>
+#include <linux/wait.h>
+#include <linux/android_power.h>
+#include <linux/suspend.h>
+#include <linux/syscalls.h> // sys_sync
+#include <linux/console.h>
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/freezer.h>
+#ifdef CONFIG_ANDROID_POWER_STAT
+#include <linux/proc_fs.h>
+#endif
+
+enum {
+ ANDROID_POWER_DEBUG_USER_STATE = 1U << 0,
+ ANDROID_POWER_DEBUG_EXIT_SUSPEND = 1U << 1,
+ ANDROID_POWER_DEBUG_SUSPEND = 1U << 2,
+ ANDROID_POWER_DEBUG_USER_WAKE_LOCK = 1U << 3,
+ ANDROID_POWER_DEBUG_WAKE_LOCK = 1U << 4,
+};
+static int android_power_debug_mask =
+ ANDROID_POWER_DEBUG_USER_STATE | ANDROID_POWER_DEBUG_EXIT_SUSPEND;
+module_param_named(debug_mask, android_power_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define ANDROID_POWER_TEST_EARLY_SUSPEND 0
+
+MODULE_DESCRIPTION("OMAP CSMI Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+#define ANDROID_SUSPEND_CONSOLE (MAX_NR_CONSOLES-2)
+
+static spinlock_t g_list_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_MUTEX(g_early_suspend_lock);
+
+wait_queue_head_t g_wait_queue;
+
+static LIST_HEAD(g_inactive_locks);
+static LIST_HEAD(g_active_idle_wake_locks);
+static LIST_HEAD(g_active_partial_wake_locks);
+static LIST_HEAD(g_active_full_wake_locks);
+static LIST_HEAD(g_early_suspend_handlers);
+static enum {
+ USER_AWAKE,
+ USER_NOTIFICATION,
+ USER_SLEEP
+} g_user_suspend_state;
+static int g_current_event_num;
+static struct workqueue_struct *g_suspend_work_queue;
+static void android_power_suspend(struct work_struct *work);
+static void android_power_wakeup_locked(int notification, ktime_t time);
+static DECLARE_WORK(g_suspend_work, android_power_suspend);
+static int g_max_user_lockouts = 16;
+
+//static const char g_free_user_lockout_name[] = "free_user";
+static struct {
+ enum {
+ USER_WAKE_LOCK_INACTIVE,
+ USER_WAKE_LOCK_PARTIAL,
+ USER_WAKE_LOCK_FULL
+ } state;
+ android_suspend_lock_t suspend_lock;
+ char name_buffer[32];
+} *g_user_wake_locks;
+#ifdef CONFIG_ANDROID_POWER_STAT
+android_suspend_lock_t g_deleted_wake_locks;
+android_suspend_lock_t g_no_wake_locks;
+#endif
+static struct kobject *android_power_kobj;
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+static wait_queue_head_t fb_state_wq;
+static spinlock_t fb_state_lock = SPIN_LOCK_UNLOCKED;
+int fb_state;
+#endif
+
+#if 0
+android_suspend_lock_t *android_allocate_suspend_lock(const char *debug_name)
+{
+ unsigned long irqflags;
+ struct android_power *e;
+
+ e = kzalloc(sizeof(*e), GFP_KERNEL);
+ if(e == NULL) {
+ printk("android_power_allocate: kzalloc failed\n");
+ return NULL;
+ }
+ e->name = debug_name;
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ list_add(&e->link, &g_allocated);
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return e;
+}
+#endif
+
+static int android_init_suspend_lock_internal(
+ android_suspend_lock_t *lock, int has_spin_lock)
+{
+ unsigned long irqflags;
+
+ if(lock->name == NULL) {
+ printk(KERN_ERR "android_init_suspend_lock: error name=NULL, "
+ "lock=%p\n", lock);
+ dump_stack();
+ return -EINVAL;
+ }
+
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_init_suspend_lock name=%s\n",
+ lock->name);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ lock->stat.count = 0;
+ lock->stat.expire_count = 0;
+ lock->stat.total_time = ktime_set(0, 0);
+ lock->stat.max_time = ktime_set(0, 0);
+ lock->stat.last_time = ktime_set(0, 0);
+#endif
+ lock->flags = 0;
+
+ INIT_LIST_HEAD(&lock->link);
+ if (!has_spin_lock)
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ list_add(&lock->link, &g_inactive_locks);
+ if (!has_spin_lock)
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+// if(lock->flags & ANDROID_SUSPEND_LOCK_FLAG_USER_VISIBLE_MASK) {
+// sysfs_create_file(struct kobject * k, const struct attribute * a)
+// }
+ return 0;
+}
+
+int android_init_suspend_lock(android_suspend_lock_t *lock)
+{
+ return android_init_suspend_lock_internal(lock, 0);
+}
+
+void android_uninit_suspend_lock(android_suspend_lock_t *lock)
+{
+ unsigned long irqflags;
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_uninit_suspend_lock name=%s\n",
+ lock->name);
+ spin_lock_irqsave(&g_list_lock, irqflags);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ if(lock->stat.count) {
+ if(g_deleted_wake_locks.stat.count == 0) {
+ g_deleted_wake_locks.name = "deleted_wake_locks";
+ android_init_suspend_lock_internal(
+ &g_deleted_wake_locks, 1);
+ }
+ g_deleted_wake_locks.stat.count += lock->stat.count;
+ g_deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
+ g_deleted_wake_locks.stat.total_time = ktime_add(g_deleted_wake_locks.stat.total_time, lock->stat.total_time);
+ g_deleted_wake_locks.stat.max_time = ktime_add(g_deleted_wake_locks.stat.max_time, lock->stat.max_time);
+ }
+#endif
+ list_del(&lock->link);
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+void android_lock_idle(android_suspend_lock_t *lock)
+{
+ unsigned long irqflags;
+ spin_lock_irqsave(&g_list_lock, irqflags);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) {
+ lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE;
+ lock->stat.last_time = ktime_get();
+ }
+#endif
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_power: acquire idle wake lock: %s\n",
+ lock->name);
+ lock->expires = INT_MAX;
+ lock->flags &= ~ANDROID_SUSPEND_LOCK_AUTO_EXPIRE;
+ list_del(&lock->link);
+ list_add(&lock->link, &g_active_idle_wake_locks);
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+void android_lock_idle_auto_expire(android_suspend_lock_t *lock, int timeout)
+{
+ unsigned long irqflags;
+ spin_lock_irqsave(&g_list_lock, irqflags);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) {
+ lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE;
+ lock->stat.last_time = ktime_get();
+ }
+#endif
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_power: acquire idle wake lock: %s, "
+ "timeout %d.%03lu\n", lock->name, timeout / HZ,
+ (timeout % HZ) * MSEC_PER_SEC / HZ);
+ lock->expires = jiffies + timeout;
+ lock->flags |= ANDROID_SUSPEND_LOCK_AUTO_EXPIRE;
+ list_del(&lock->link);
+ list_add(&lock->link, &g_active_idle_wake_locks);
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+void android_lock_suspend(android_suspend_lock_t *lock)
+{
+ unsigned long irqflags;
+ spin_lock_irqsave(&g_list_lock, irqflags);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) {
+ lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE;
+ lock->stat.last_time = ktime_get();
+ }
+#endif
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_power: acquire wake lock: %s\n",
+ lock->name);
+ lock->expires = INT_MAX;
+ lock->flags &= ~ANDROID_SUSPEND_LOCK_AUTO_EXPIRE;
+ list_del(&lock->link);
+ list_add(&lock->link, &g_active_partial_wake_locks);
+ g_current_event_num++;
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+void android_lock_suspend_auto_expire(android_suspend_lock_t *lock, int timeout)
+{
+ unsigned long irqflags;
+ spin_lock_irqsave(&g_list_lock, irqflags);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) {
+ lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE;
+ lock->stat.last_time = ktime_get();
+ }
+#endif
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_power: acquire wake lock: %s, "
+ "timeout %d.%03lu\n", lock->name, timeout / HZ,
+ (timeout % HZ) * MSEC_PER_SEC / HZ);
+ lock->expires = jiffies + timeout;
+ lock->flags |= ANDROID_SUSPEND_LOCK_AUTO_EXPIRE;
+ list_del(&lock->link);
+ list_add(&lock->link, &g_active_partial_wake_locks);
+ g_current_event_num++;
+ wake_up(&g_wait_queue);
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+void android_lock_partial_suspend_auto_expire(android_suspend_lock_t *lock, int timeout)
+{
+ unsigned long irqflags;
+ spin_lock_irqsave(&g_list_lock, irqflags);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) {
+ lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE;
+ lock->stat.last_time = ktime_get();
+ }
+#endif
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_power: acquire full wake lock: %s, "
+ "timeout %d.%03lu\n", lock->name, timeout / HZ,
+ (timeout % HZ) * MSEC_PER_SEC / HZ);
+ lock->expires = jiffies + timeout;
+ lock->flags |= ANDROID_SUSPEND_LOCK_AUTO_EXPIRE;
+ list_del(&lock->link);
+ list_add(&lock->link, &g_active_full_wake_locks);
+ g_current_event_num++;
+ wake_up(&g_wait_queue);
+ android_power_wakeup_locked(1, ktime_get());
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+#ifdef CONFIG_ANDROID_POWER_STAT
+static int print_lock_stat(char *buf, android_suspend_lock_t *lock)
+{
+ ktime_t active_time;
+ if(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)
+ active_time = ktime_sub(ktime_get(), lock->stat.last_time);
+ else
+ active_time = ktime_set(0, 0);
+ return sprintf(buf, "\"%s\"\t%d\t%d\t%lld\t%lld\t%lld\t%lld\n",
+ lock->name,
+ lock->stat.count, lock->stat.expire_count,
+ ktime_to_ns(active_time),
+ ktime_to_ns(lock->stat.total_time),
+ ktime_to_ns(lock->stat.max_time),
+ ktime_to_ns(lock->stat.last_time));
+}
+
+
+static int wakelocks_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ unsigned long irqflags;
+ android_suspend_lock_t *lock;
+ int len = 0;
+ char *p = page;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+
+ p += sprintf(p, "name\tcount\texpire_count\tactive_since\ttotal_time\tmax_time\tlast_change\n");
+ list_for_each_entry(lock, &g_inactive_locks, link) {
+ p += print_lock_stat(p, lock);
+ }
+ list_for_each_entry(lock, &g_active_partial_wake_locks, link) {
+ p += print_lock_stat(p, lock);
+ }
+ list_for_each_entry(lock, &g_active_full_wake_locks, link) {
+ p += print_lock_stat(p, lock);
+ }
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+
+
+ *start = page + off;
+
+ len = p - page;
+ if (len > off)
+ len -= off;
+ else
+ len = 0;
+
+ return len < count ? len : count;
+}
+
+static void android_unlock_suspend_stat_locked(android_suspend_lock_t *lock)
+{
+ if(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE) {
+ ktime_t duration;
+ lock->flags &= ~ANDROID_SUSPEND_LOCK_ACTIVE;
+ lock->stat.count++;
+ duration = ktime_sub(ktime_get(), lock->stat.last_time);
+ lock->stat.total_time = ktime_add(lock->stat.total_time, duration);
+ if(ktime_to_ns(duration) > ktime_to_ns(lock->stat.max_time))
+ lock->stat.max_time = duration;
+ lock->stat.last_time = ktime_get();
+ }
+}
+#endif
+
+void android_unlock_suspend(android_suspend_lock_t *lock)
+{
+ int had_full_wake_locks;
+ unsigned long irqflags;
+ spin_lock_irqsave(&g_list_lock, irqflags);
+#ifdef CONFIG_ANDROID_POWER_STAT
+ android_unlock_suspend_stat_locked(lock);
+#endif
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk(KERN_INFO "android_power: release wake lock: %s\n",
+ lock->name);
+ lock->flags &= ~ANDROID_SUSPEND_LOCK_AUTO_EXPIRE;
+ had_full_wake_locks = !list_empty(&g_active_full_wake_locks);
+ list_del(&lock->link);
+ list_add(&lock->link, &g_inactive_locks);
+ wake_up(&g_wait_queue);
+ if(had_full_wake_locks && list_empty(&g_active_full_wake_locks)) {
+ printk("android_unlock_suspend: released at %lld\n", ktime_to_ns(ktime_get()));
+ if(g_user_suspend_state == USER_NOTIFICATION) {
+ printk("android sleep state %d->%d at %lld\n", g_user_suspend_state, USER_SLEEP, ktime_to_ns(ktime_get()));
+ g_user_suspend_state = USER_SLEEP;
+ queue_work(g_suspend_work_queue, &g_suspend_work);
+ }
+ }
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+static void android_power_wakeup_locked(int notification, ktime_t time)
+{
+ int new_state = (notification == 0) ? USER_AWAKE : USER_NOTIFICATION;
+
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_USER_STATE) {
+ struct timespec ts;
+ struct rtc_time tm;
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ printk(KERN_INFO "android_power: wakeup (%d->%d) at %lld "
+ "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
+ g_user_suspend_state, new_state, ktime_to_ns(time),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+ }
+
+ if(new_state >= g_user_suspend_state) {
+ return;
+ }
+ g_user_suspend_state = new_state;
+ g_current_event_num++;
+ wake_up(&g_wait_queue);
+}
+
+static void android_power_wakeup(void)
+{
+ unsigned long irqflags;
+
+ ktime_t ktime_now;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ ktime_now = ktime_get();
+ android_power_wakeup_locked(0, ktime_now);
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+}
+
+static void android_power_request_sleep(void)
+{
+ unsigned long irqflags;
+ int already_suspended;
+ android_suspend_lock_t *lock, *next_lock;
+
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_USER_STATE) {
+ ktime_t ktime_now;
+ struct timespec ts;
+ struct rtc_time tm;
+ ktime_now = ktime_get();
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ printk(KERN_INFO "android_power: sleep (%d->%d) at %lld "
+ "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
+ g_user_suspend_state, USER_SLEEP, ktime_to_ns(ktime_now),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+ }
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ already_suspended = g_user_suspend_state == USER_SLEEP;
+ if(!already_suspended) {
+ g_user_suspend_state = USER_SLEEP;
+ }
+
+ list_for_each_entry_safe(lock, next_lock, &g_active_full_wake_locks, link) {
+#ifdef CONFIG_ANDROID_POWER_STAT
+ android_unlock_suspend_stat_locked(lock);
+#endif
+ list_del(&lock->link);
+ list_add(&lock->link, &g_inactive_locks);
+ printk("android_power_suspend: aborted full wake lock %s\n", lock->name);
+ }
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ queue_work(g_suspend_work_queue, &g_suspend_work);
+}
+
+void android_register_early_suspend(android_early_suspend_t *handler)
+{
+ struct list_head *pos;
+
+ mutex_lock(&g_early_suspend_lock);
+ list_for_each(pos, &g_early_suspend_handlers) {
+ android_early_suspend_t *e = list_entry(pos, android_early_suspend_t, link);
+ if(e->level > handler->level)
+ break;
+ }
+ list_add_tail(&handler->link, pos);
+ mutex_unlock(&g_early_suspend_lock);
+}
+
+void android_unregister_early_suspend(android_early_suspend_t *handler)
+{
+ mutex_lock(&g_early_suspend_lock);
+ list_del(&handler->link);
+ mutex_unlock(&g_early_suspend_lock);
+}
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+static int orig_fgconsole;
+static void console_early_suspend(android_early_suspend_t *h)
+{
+ acquire_console_sem();
+ orig_fgconsole = fg_console;
+ if (vc_allocate(ANDROID_SUSPEND_CONSOLE))
+ goto err;
+ if (set_console(ANDROID_SUSPEND_CONSOLE))
+ goto err;
+ release_console_sem();
+
+ if (vt_waitactive(ANDROID_SUSPEND_CONSOLE))
+ pr_warning("console_early_suspend: Can't switch VCs.\n");
+ return;
+err:
+ pr_warning("console_early_suspend: Can't set console\n");
+ release_console_sem();
+}
+
+static void console_late_resume(android_early_suspend_t *h)
+{
+ int ret;
+ acquire_console_sem();
+ ret = set_console(orig_fgconsole);
+ release_console_sem();
+ if (ret) {
+ pr_warning("console_late_resume: Can't set console.\n");
+ return;
+ }
+
+ if (vt_waitactive(orig_fgconsole))
+ pr_warning("console_late_resume: Can't switch VCs.\n");
+}
+
+static android_early_suspend_t console_early_suspend_desc = {
+ .level = ANDROID_EARLY_SUSPEND_LEVEL_CONSOLE_SWITCH,
+ .suspend = console_early_suspend,
+ .resume = console_late_resume,
+};
+#else
+/* tell userspace to stop drawing, wait for it to stop */
+static void stop_drawing_early_suspend(android_early_suspend_t *h)
+{
+ int ret;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
+ fb_state = ANDROID_REQUEST_STOP_DRAWING;
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+
+ wake_up_all(&fb_state_wq);
+ ret = wait_event_timeout(fb_state_wq,
+ fb_state == ANDROID_STOPPED_DRAWING,
+ HZ);
+ if (unlikely(fb_state != ANDROID_STOPPED_DRAWING))
+ printk(KERN_WARNING "android_power: timeout waiting for "
+ "userspace to stop drawing\n");
+}
+
+/* tell userspace to start drawing */
+static void start_drawing_late_resume(android_early_suspend_t *h)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
+ fb_state = ANDROID_DRAWING_OK;
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+ wake_up(&fb_state_wq);
+}
+
+static android_early_suspend_t stop_drawing_early_suspend_desc = {
+ .level = ANDROID_EARLY_SUSPEND_LEVEL_CONSOLE_SWITCH,
+ .suspend = stop_drawing_early_suspend,
+ .resume = start_drawing_late_resume,
+};
+#endif
+
+#if ANDROID_POWER_TEST_EARLY_SUSPEND
+
+typedef struct
+{
+ android_early_suspend_t h;
+ const char *string;
+} early_suspend_test_t;
+
+static void early_suspend_test(android_early_suspend_t *h)
+{
+ early_suspend_test_t *est = container_of(h, early_suspend_test_t, h);
+ printk("early suspend %s (l %d)\n", est->string, h->level);
+}
+
+static void late_resume_test(android_early_suspend_t *h)
+{
+ early_suspend_test_t *est = container_of(h, early_suspend_test_t, h);
+ printk("late resume %s (l %d)\n", est->string, h->level);
+}
+
+#define EARLY_SUSPEND_TEST_ENTRY(ilevel, istring) \
+{ \
+ .h = { \
+ .level = ilevel, \
+ .suspend = early_suspend_test, \
+ .resume = late_resume_test \
+ }, \
+ .string = istring \
+}
+static early_suspend_test_t early_suspend_tests[] = {
+ EARLY_SUSPEND_TEST_ENTRY(10, "1"),
+ EARLY_SUSPEND_TEST_ENTRY(5, "2"),
+ EARLY_SUSPEND_TEST_ENTRY(10, "3"),
+ EARLY_SUSPEND_TEST_ENTRY(15, "4"),
+ EARLY_SUSPEND_TEST_ENTRY(8, "5")
+};
+
+#endif
+
+static int get_wait_timeout(int print_locks, int state, struct list_head *list_head)
+{
+ unsigned long irqflags;
+ android_suspend_lock_t *lock, *next;
+ int max_timeout = 0;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ list_for_each_entry_safe(lock, next, list_head, link) {
+ if(lock->flags & ANDROID_SUSPEND_LOCK_AUTO_EXPIRE) {
+ int timeout = lock->expires - (int)jiffies;
+ if(timeout <= 0) {
+ lock->flags &= ~ANDROID_SUSPEND_LOCK_AUTO_EXPIRE;
+#ifdef CONFIG_ANDROID_POWER_STAT
+ lock->stat.expire_count++;
+ android_unlock_suspend_stat_locked(lock);
+#endif
+ list_del(&lock->link);
+ list_add(&lock->link, &g_inactive_locks);
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_WAKE_LOCK)
+ printk("expired wake lock %s\n", lock->name);
+ }
+ else {
+ if(timeout > max_timeout)
+ max_timeout = timeout;
+ if(print_locks)
+ printk("active wake lock %s, time left %d\n", lock->name, timeout);
+ }
+ }
+ else {
+ if(print_locks)
+ printk("active wake lock %s\n", lock->name);
+ }
+ }
+ if(g_user_suspend_state != state || list_empty(list_head))
+ max_timeout = -1;
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return max_timeout;
+}
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+static int android_power_class_suspend(struct sys_device *sdev, pm_message_t state)
+{
+ int rv = 0;
+ unsigned long irqflags;
+
+ printk("android_power_suspend: enter\n");
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ if(!list_empty(&g_active_partial_wake_locks)) {
+ printk("android_power_suspend: abort for partial wakeup\n");
+ rv = -EAGAIN;
+ }
+ if(g_user_suspend_state != USER_SLEEP) {
+ printk("android_power_suspend: abort for full wakeup\n");
+ rv = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return rv;
+}
+
+static int android_power_device_suspend(struct sys_device *sdev, pm_message_t state)
+{
+ int rv = 0;
+ unsigned long irqflags;
+
+ printk("android_power_device_suspend: enter\n");
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ if(!list_empty(&g_active_partial_wake_locks)) {
+ printk("android_power_device_suspend: abort for partial wakeup\n");
+ rv = -EAGAIN;
+ }
+ if(g_user_suspend_state != USER_SLEEP) {
+ printk("android_power_device_suspend: abort for full wakeup\n");
+ rv = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return rv;
+}
+#endif
+
+int android_power_is_driver_suspended(void)
+{
+ return (get_wait_timeout(0, USER_SLEEP, &g_active_partial_wake_locks) < 0) && (g_user_suspend_state == USER_SLEEP);
+}
+
+int android_power_is_low_power_idle_ok(void)
+{
+ get_wait_timeout(0, USER_SLEEP, &g_active_idle_wake_locks);
+ return list_empty(&g_active_idle_wake_locks);
+}
+
+static void android_power_suspend(struct work_struct *work)
+{
+ int entry_event_num;
+ int ret;
+ int wait = 0;
+ android_early_suspend_t *pos;
+ int print_locks = 0;
+ unsigned long irqflags;
+
+ while(g_user_suspend_state != USER_AWAKE) {
+ while(g_user_suspend_state == USER_NOTIFICATION) {
+ wait = get_wait_timeout(print_locks, USER_NOTIFICATION, &g_active_full_wake_locks);
+ if(wait < 0)
+ break;
+ if(wait)
+ wait_event_interruptible_timeout(g_wait_queue, get_wait_timeout(0, USER_NOTIFICATION, &g_active_full_wake_locks) != wait, wait);
+ }
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ if(g_user_suspend_state == USER_NOTIFICATION && list_empty(&g_active_full_wake_locks)) {
+ printk("android sleep state %d->%d at %lld\n", g_user_suspend_state, USER_SLEEP, ktime_to_ns(ktime_get()));
+ g_user_suspend_state = USER_SLEEP;
+ }
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ wait = 0;
+ if(g_user_suspend_state == USER_AWAKE) {
+ printk("android_power_suspend: suspend aborted\n");
+ return;
+ }
+
+ mutex_lock(&g_early_suspend_lock);
+ //printk("android_power_suspend: call early suspend handlers\n");
+ list_for_each_entry(pos, &g_early_suspend_handlers, link) {
+ if(pos->suspend != NULL)
+ pos->suspend(pos);
+ }
+ //printk("android_power_suspend: call early suspend handlers\n");
+
+ //printk("android_power_suspend: enter\n");
+
+ sys_sync();
+
+ while(g_user_suspend_state == USER_SLEEP) {
+ //printk("android_power_suspend: enter wait (%d)\n", wait);
+ if(wait) {
+ wait_event_interruptible_timeout(g_wait_queue, g_user_suspend_state != USER_SLEEP, wait);
+ wait = 0;
+ }
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_SUSPEND)
+ print_locks = 1;
+ while(1) {
+ wait = get_wait_timeout(print_locks, USER_SLEEP, &g_active_partial_wake_locks);
+ print_locks = 0;
+ if(wait < 0)
+ break;
+ if(wait)
+ wait_event_interruptible_timeout(g_wait_queue, get_wait_timeout(0, USER_SLEEP, &g_active_partial_wake_locks) != wait, wait);
+ else
+ wait_event_interruptible(g_wait_queue, get_wait_timeout(0, USER_SLEEP, &g_active_partial_wake_locks) != wait);
+ }
+ wait = 0;
+ //printk("android_power_suspend: exit wait\n");
+ entry_event_num = g_current_event_num;
+ if(g_user_suspend_state != USER_SLEEP)
+ break;
+ sys_sync();
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_SUSPEND)
+ printk(KERN_INFO "android_power_suspend: enter suspend\n");
+ ret = pm_suspend(PM_SUSPEND_MEM);
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_EXIT_SUSPEND) {
+ struct timespec ts;
+ struct rtc_time tm;
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ printk("android_power_suspend: exit suspend, ret = %d "
+ "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+ }
+ if(g_current_event_num == entry_event_num) {
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_SUSPEND)
+ printk(KERN_INFO "android_power_suspend: pm_suspend returned with no event\n");
+ wait = HZ / 2;
+#ifdef CONFIG_ANDROID_POWER_STAT
+ if(g_no_wake_locks.stat.count == 0) {
+ g_no_wake_locks.name = "unknown_wakeups";
+ android_init_suspend_lock(&g_no_wake_locks);
+ }
+ g_no_wake_locks.stat.count++;
+ g_no_wake_locks.stat.total_time = ktime_add(
+ g_no_wake_locks.stat.total_time,
+ ktime_set(0, 500 * NSEC_PER_MSEC));
+ g_no_wake_locks.stat.max_time =
+ ktime_set(0, 500 * NSEC_PER_MSEC);
+#endif
+ }
+ }
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_USER_STATE)
+ printk("android_power_suspend: done\n");
+ //printk("android_power_suspend: call late resume handlers\n");
+ list_for_each_entry_reverse(pos, &g_early_suspend_handlers, link) {
+ if(pos->resume != NULL)
+ pos->resume(pos);
+ }
+ //printk("android_power_suspend: call late resume handlers\n");
+ mutex_unlock(&g_early_suspend_lock);
+ }
+}
+
+#if 0
+struct sysdev_class android_power_sysclass = {
+ set_kset_name("android_power"),
+ .suspend = android_power_class_suspend
+};
+static struct sysdev_class *g_android_power_sysclass = NULL;
+
+static struct {
+ struct sys_device sysdev;
+// omap_csmi_gsm_image_info_t *pdata;
+} android_power_device = {
+ .sysdev = {
+ .id = 0,
+ .cls = &android_power_sysclass,
+// .suspend = android_power_device_suspend
+ },
+// .pdata = &g_gsm_image_info
+};
+
+struct sysdev_class *android_power_get_sysclass(void)
+{
+ return g_android_power_sysclass;
+}
+#endif
+
+static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+ char * s = buf;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ s += sprintf(s, "%d-%d-%d\n", g_user_suspend_state, list_empty(&g_active_full_wake_locks), list_empty(&g_active_partial_wake_locks));
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return (s - buf);
+}
+
+static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
+{
+ if(n >= strlen("standby") &&
+ strncmp(buf, "standby", strlen("standby")) == 0) {
+ android_power_request_sleep();
+ wait_event_interruptible(g_wait_queue, g_user_suspend_state == USER_AWAKE);
+ return n;
+ }
+ if(n >= strlen("wake") &&
+ strncmp(buf, "wake", strlen("wake")) == 0) {
+ android_power_wakeup();
+ return n;
+ }
+ printk("android_power state_store: invalid argument\n");
+ return -EINVAL;
+}
+
+static ssize_t request_state_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+ char * s = buf;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ if(g_user_suspend_state == USER_AWAKE)
+ s += sprintf(s, "wake\n");
+ else if(g_user_suspend_state == USER_NOTIFICATION)
+ s += sprintf(s, "standby (w/full wake lock)\n");
+ else
+ s += sprintf(s, "standby\n");
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return (s - buf);
+}
+
+static ssize_t request_state_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
+{
+ if(n >= strlen("standby") &&
+ strncmp(buf, "standby", strlen("standby")) == 0) {
+ android_power_request_sleep();
+ return n;
+ }
+ if(n >= strlen("wake") &&
+ strncmp(buf, "wake", strlen("wake")) == 0) {
+ android_power_wakeup();
+ return n;
+ }
+ printk("android_power state_store: invalid argument\n");
+ return -EINVAL;
+}
+
+
+static int lookup_wake_lock_name(const char *buf, size_t n, int allocate, int *timeout)
+{
+ int i;
+ int free_index = -1;
+ int inactive_index = -1;
+ int expires_index = -1;
+ int expires_time = INT_MAX;
+ char *tmp_buf[64];
+ char name[32];
+ u64 nanoseconds;
+ int num_arg;
+
+ if(n <= 0)
+ return -EINVAL;
+ if(n >= sizeof(tmp_buf))
+ return -EOVERFLOW;
+ if(n == sizeof(tmp_buf) - 1 && buf[n - 1] != '\0')
+ return -EOVERFLOW;
+
+ memcpy(tmp_buf, buf, n);
+ if(tmp_buf[n - 1] != '\0')
+ tmp_buf[n] = '\0';
+
+ num_arg = sscanf(buf, "%31s %llu", name, &nanoseconds);
+ if(num_arg < 1)
+ return -EINVAL;
+
+ if(strlen(name) >= sizeof(g_user_wake_locks[i].name_buffer))
+ return -EOVERFLOW;
+
+ if(timeout != NULL) {
+ if(num_arg > 1) {
+ do_div(nanoseconds, (NSEC_PER_SEC / HZ));
+ if(nanoseconds <= 0)
+ nanoseconds = 1;
+ *timeout = nanoseconds;
+ }
+ else
+ *timeout = 0;
+ }
+
+ for(i = 0; i < g_max_user_lockouts; i++) {
+ if(strcmp(g_user_wake_locks[i].name_buffer, name) == 0)
+ return i;
+ if(g_user_wake_locks[i].name_buffer[0] == '\0')
+ free_index = i;
+ else if(g_user_wake_locks[i].state == USER_WAKE_LOCK_INACTIVE)
+ inactive_index = i;
+ else if(g_user_wake_locks[i].suspend_lock.expires < expires_time)
+ expires_index = i;
+ }
+ if(allocate) {
+ if(free_index >= 0)
+ i = free_index;
+ else if(inactive_index >= 0)
+ i = inactive_index;
+ else if(expires_index >= 0) {
+ i = expires_index;
+ printk("lookup_wake_lock_name: overwriting expired lock, %s\n", g_user_wake_locks[i].name_buffer);
+ }
+ else {
+ i = 0;
+ printk("lookup_wake_lock_name: overwriting active lock, %s\n", g_user_wake_locks[i].name_buffer);
+ }
+ strcpy(g_user_wake_locks[i].name_buffer, name);
+ return i;
+ }
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_USER_WAKE_LOCK)
+ printk(KERN_INFO "lookup_wake_lock_name: %s not found\n", name);
+ return -EINVAL;
+}
+
+static ssize_t acquire_full_wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+ int i;
+ char * s = buf;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ for(i = 0; i < g_max_user_lockouts; i++) {
+ if(g_user_wake_locks[i].name_buffer[0] != '\0' && g_user_wake_locks[i].state == USER_WAKE_LOCK_FULL)
+ s += sprintf(s, "%s ", g_user_wake_locks[i].name_buffer);
+ }
+ s += sprintf(s, "\n");
+
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return (s - buf);
+}
+
+static ssize_t acquire_full_wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
+{
+ int i;
+ unsigned long irqflags;
+ int timeout;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ i = lookup_wake_lock_name(buf, n, 1, &timeout);
+ if(i >= 0)
+ g_user_wake_locks[i].state = USER_WAKE_LOCK_FULL;
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ if(i < 0)
+ return i;
+
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_USER_WAKE_LOCK)
+ printk(KERN_INFO "acquire_full_wake_lock_store: %s, size %d\n",
+ g_user_wake_locks[i].name_buffer, n);
+
+ //android_lock_partial_suspend_auto_expire(&g_user_wake_locks[i].suspend_lock, ktime_to_timespec(g_auto_off_timeout).tv_sec * HZ);
+ if(timeout == 0)
+ timeout = INT_MAX;
+ android_lock_partial_suspend_auto_expire(&g_user_wake_locks[i].suspend_lock, timeout);
+
+ return n;
+}
+
+static ssize_t acquire_partial_wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+ int i;
+ char * s = buf;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ for(i = 0; i < g_max_user_lockouts; i++) {
+ if(g_user_wake_locks[i].name_buffer[0] != '\0' && g_user_wake_locks[i].state == USER_WAKE_LOCK_PARTIAL)
+ s += sprintf(s, "%s ", g_user_wake_locks[i].name_buffer);
+ }
+ s += sprintf(s, "\n");
+
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return (s - buf);
+}
+
+static ssize_t acquire_partial_wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
+{
+ int i;
+ unsigned long irqflags;
+ int timeout;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ i = lookup_wake_lock_name(buf, n, 1, &timeout);
+ if(i >= 0)
+ g_user_wake_locks[i].state = USER_WAKE_LOCK_PARTIAL;
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ if(i < 0)
+ return 0;
+
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_USER_WAKE_LOCK)
+ printk(KERN_INFO "acquire_partial_wake_lock_store: %s, "
+ "size %d\n", g_user_wake_locks[i].name_buffer, n);
+
+ if(timeout)
+ android_lock_suspend_auto_expire(&g_user_wake_locks[i].suspend_lock, timeout);
+ else
+ android_lock_suspend(&g_user_wake_locks[i].suspend_lock);
+
+ return n;
+}
+
+
+static ssize_t release_wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+ int i;
+ char * s = buf;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ for(i = 0; i < g_max_user_lockouts; i++) {
+ if(g_user_wake_locks[i].name_buffer[0] != '\0' && g_user_wake_locks[i].state == USER_WAKE_LOCK_INACTIVE)
+ s += sprintf(s, "%s ", g_user_wake_locks[i].name_buffer);
+ }
+ s += sprintf(s, "\n");
+
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+ return (s - buf);
+}
+
+static ssize_t release_wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
+{
+ int i;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&g_list_lock, irqflags);
+ i = lookup_wake_lock_name(buf, n, 1, NULL);
+ if(i >= 0) {
+ g_user_wake_locks[i].state = USER_WAKE_LOCK_INACTIVE;
+ }
+ spin_unlock_irqrestore(&g_list_lock, irqflags);
+
+ if(i < 0)
+ return i;
+
+ if (android_power_debug_mask & ANDROID_POWER_DEBUG_USER_WAKE_LOCK)
+ printk(KERN_INFO "release_wake_lock_store: %s, size %d\n",
+ g_user_wake_locks[i].name_buffer, n);
+
+ android_unlock_suspend(&g_user_wake_locks[i].suspend_lock);
+ return n;
+}
+
+
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+static ssize_t wait_for_fb_sleep_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ char * s = buf;
+ int ret;
+
+ ret = wait_event_interruptible(fb_state_wq,
+ fb_state != ANDROID_DRAWING_OK);
+ if (ret && fb_state == ANDROID_DRAWING_OK)
+ return ret;
+ else
+ s += sprintf(buf, "sleeping");
+ return (s - buf);
+}
+
+static ssize_t wait_for_fb_wake_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ char * s = buf;
+ int ret;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
+ if (fb_state == ANDROID_REQUEST_STOP_DRAWING) {
+ fb_state = ANDROID_STOPPED_DRAWING;
+ wake_up(&fb_state_wq);
+ }
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+
+ ret = wait_event_interruptible(fb_state_wq,
+ fb_state == ANDROID_DRAWING_OK);
+ if (ret && fb_state != ANDROID_DRAWING_OK)
+ return ret;
+ else
+ s += sprintf(buf, "awake");
+
+ return (s - buf);
+}
+#endif
+
+#define android_power_attr(_name) \
+static struct kobj_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = 0664, \
+ }, \
+ .show = _name##_show, \
+ .store = _name##_store, \
+}
+
+#define android_power_ro_attr(_name) \
+static struct kobj_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = 0444, \
+ }, \
+ .show = _name##_show, \
+ .store = NULL, \
+}
+
+android_power_attr(state);
+android_power_attr(request_state);
+android_power_attr(acquire_full_wake_lock);
+android_power_attr(acquire_partial_wake_lock);
+android_power_attr(release_wake_lock);
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+android_power_ro_attr(wait_for_fb_sleep);
+android_power_ro_attr(wait_for_fb_wake);
+#endif
+
+static struct attribute * g[] = {
+ &state_attr.attr,
+ &request_state_attr.attr,
+ &acquire_full_wake_lock_attr.attr,
+ &acquire_partial_wake_lock_attr.attr,
+ &release_wake_lock_attr.attr,
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+ &wait_for_fb_sleep_attr.attr,
+ &wait_for_fb_wake_attr.attr,
+#endif
+ NULL,
+};
+
+static struct attribute_group attr_group = {
+ .attrs = g,
+};
+
+#if 0
+// test code when there is no platform suspend
+
+static android_suspend_lock_t test_pm_ops_suspend_lock = {
+ .name = "test_pm_ops"
+};
+
+int test_pm_op_enter(suspend_state_t state)
+{
+ printk("test_pm_op_enter reached\n");
+ android_lock_suspend(&test_pm_ops_suspend_lock);
+ printk("test_pm_op_enter returned\n");
+ return 0;
+}
+
+void test_pm_ops_late_resume_handler(android_early_suspend_t *h)
+{
+ printk("test_pm_ops_late_resume_handler reached\n");
+ android_unlock_suspend(&test_pm_ops_suspend_lock);
+ printk("test_pm_ops_late_resume_handler returned\n");
+}
+
+static struct pm_ops test_pm_ops = {
+ .enter = test_pm_op_enter
+};
+
+static android_early_suspend_t test_pm_ops_early_suspend_handler = {
+ .resume = test_pm_ops_late_resume_handler
+};
+#endif
+
+static int __init android_power_init(void)
+{
+ int ret;
+ int i;
+
+#if 0
+ if(pm_ops == NULL) {
+ printk("android_power_init no pm_ops, installing test code\n");
+ pm_set_ops(&test_pm_ops);
+ android_init_suspend_lock(&test_pm_ops_suspend_lock);
+ android_register_early_suspend(&test_pm_ops_early_suspend_handler);
+ }
+#endif
+
+#ifdef CONFIG_ANDROID_POWER_STAT
+ g_deleted_wake_locks.stat.count = 0;
+#endif
+ init_waitqueue_head(&g_wait_queue);
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+ init_waitqueue_head(&fb_state_wq);
+ fb_state = ANDROID_DRAWING_OK;
+#endif
+
+ g_user_wake_locks = kzalloc(sizeof(*g_user_wake_locks) * g_max_user_lockouts, GFP_KERNEL);
+ if(g_user_wake_locks == NULL) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ for(i = 0; i < g_max_user_lockouts; i++) {
+ g_user_wake_locks[i].suspend_lock.name = g_user_wake_locks[i].name_buffer;
+ android_init_suspend_lock(&g_user_wake_locks[i].suspend_lock);
+ }
+
+ g_suspend_work_queue = create_workqueue("suspend");
+ if(g_suspend_work_queue == NULL) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ android_power_kobj = kobject_create_and_add("android_power", NULL);
+ if (android_power_kobj == NULL) {
+ printk("android_power_init: subsystem_register failed\n");
+ ret = -ENOMEM;
+ goto err3;
+ }
+ ret = sysfs_create_group(android_power_kobj, &attr_group);
+ if(ret) {
+ printk("android_power_init: sysfs_create_group failed\n");
+ goto err4;
+ }
+#ifdef CONFIG_ANDROID_POWER_STAT
+ create_proc_read_entry("wakelocks", S_IRUGO, NULL, wakelocks_read_proc, NULL);
+#endif
+
+#if ANDROID_POWER_TEST_EARLY_SUSPEND
+ {
+ int i;
+ for(i = 0; i < sizeof(early_suspend_tests) / sizeof(early_suspend_tests[0]); i++)
+ android_register_early_suspend(&early_suspend_tests[i].h);
+ }
+#endif
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+ android_register_early_suspend(&console_early_suspend_desc);
+#else
+ android_register_early_suspend(&stop_drawing_early_suspend_desc);
+#endif
+
+#if 0
+ ret = sysdev_class_register(&android_power_sysclass);
+ if(ret) {
+ printk("android_power_init: sysdev_class_register failed\n");
+ goto err1;
+ }
+ ret = sysdev_register(&android_power_device.sysdev);
+ if(ret < 0)
+ goto err2;
+
+ g_android_power_sysclass = &android_power_sysclass;
+#endif
+ return 0;
+
+//err2:
+// sysdev_class_unregister(&android_power_sysclass);
+err4:
+ kobject_del(android_power_kobj);
+err3:
+ destroy_workqueue(g_suspend_work_queue);
+err2:
+ for(i = 0; i < g_max_user_lockouts; i++) {
+ android_uninit_suspend_lock(&g_user_wake_locks[i].suspend_lock);
+ }
+ kfree(g_user_wake_locks);
+err1:
+ return ret;
+}
+
+static void __exit android_power_exit(void)
+{
+ int i;
+// g_android_power_sysclass = NULL;
+// sysdev_unregister(&android_power_device.sysdev);
+// sysdev_class_unregister(&android_power_sysclass);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+ android_unregister_early_suspend(&console_early_suspend_desc);
+#else
+ android_unregister_early_suspend(&stop_drawing_early_suspend_desc);
+#endif
+#ifdef CONFIG_ANDROID_POWER_STAT
+ remove_proc_entry("wakelocks", NULL);
+#endif
+ sysfs_remove_group(android_power_kobj, &attr_group);
+ kobject_del(android_power_kobj);
+ destroy_workqueue(g_suspend_work_queue);
+ for(i = 0; i < g_max_user_lockouts; i++) {
+ android_uninit_suspend_lock(&g_user_wake_locks[i].suspend_lock);
+ }
+ kfree(g_user_wake_locks);
+}
+
+core_initcall(android_power_init);
+module_exit(android_power_exit);
+
+//EXPORT_SYMBOL(android_power_get_sysclass);
+EXPORT_SYMBOL(android_init_suspend_lock);
+EXPORT_SYMBOL(android_uninit_suspend_lock);
+EXPORT_SYMBOL(android_lock_suspend);
+EXPORT_SYMBOL(android_lock_suspend_auto_expire);
+EXPORT_SYMBOL(android_unlock_suspend);
+EXPORT_SYMBOL(android_power_wakeup);
+EXPORT_SYMBOL(android_register_early_suspend);
+EXPORT_SYMBOL(android_unregister_early_suspend);
+
+