| 1 | From 2d4a25785ad1aa51bad967543e1ab20e9ed4a046 Mon Sep 17 00:00:00 2001 |
| 2 | From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com> |
| 3 | Date: Tue, 7 Oct 2008 20:48:01 -0700 |
| 4 | Subject: [PATCH 067/134] PM: Implement early suspend api |
| 5 | |
| 6 | --- |
| 7 | kernel/power/Kconfig | 12 +++ |
| 8 | kernel/power/Makefile | 1 + |
| 9 | kernel/power/earlysuspend.c | 178 +++++++++++++++++++++++++++++++++++++++++++ |
| 10 | kernel/power/power.h | 6 ++ |
| 11 | 4 files changed, 197 insertions(+), 0 deletions(-) |
| 12 | create mode 100644 kernel/power/earlysuspend.c |
| 13 | |
| 14 | --- a/kernel/power/Kconfig |
| 15 | +++ b/kernel/power/Kconfig |
| 16 | @@ -119,6 +119,9 @@ config SUSPEND_FREEZER |
| 17 | config HAS_WAKELOCK |
| 18 | bool |
| 19 | |
| 20 | +config HAS_EARLYSUSPEND |
| 21 | + bool |
| 22 | + |
| 23 | config WAKELOCK |
| 24 | bool "Wake lock" |
| 25 | depends on PM && RTC_CLASS |
| 26 | @@ -135,6 +138,15 @@ config WAKELOCK_STAT |
| 27 | ---help--- |
| 28 | Report wake lock stats in /proc/wakelocks |
| 29 | |
| 30 | +config EARLYSUSPEND |
| 31 | + bool "Early suspend" |
| 32 | + depends on WAKELOCK |
| 33 | + default y |
| 34 | + select HAS_EARLYSUSPEND |
| 35 | + ---help--- |
| 36 | + Call early suspend handlers when the user requested sleep state |
| 37 | + changes. |
| 38 | + |
| 39 | config HIBERNATION |
| 40 | bool "Hibernation (aka 'suspend to disk')" |
| 41 | depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE |
| 42 | --- a/kernel/power/Makefile |
| 43 | +++ b/kernel/power/Makefile |
| 44 | @@ -7,6 +7,7 @@ obj-$(CONFIG_PM) += main.o |
| 45 | obj-$(CONFIG_PM_SLEEP) += console.o |
| 46 | obj-$(CONFIG_FREEZER) += process.o |
| 47 | obj-$(CONFIG_WAKELOCK) += wakelock.o |
| 48 | +obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o |
| 49 | obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o |
| 50 | |
| 51 | obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o |
| 52 | --- /dev/null |
| 53 | +++ b/kernel/power/earlysuspend.c |
| 54 | @@ -0,0 +1,178 @@ |
| 55 | +/* kernel/power/earlysuspend.c |
| 56 | + * |
| 57 | + * Copyright (C) 2005-2008 Google, Inc. |
| 58 | + * |
| 59 | + * This software is licensed under the terms of the GNU General Public |
| 60 | + * License version 2, as published by the Free Software Foundation, and |
| 61 | + * may be copied, distributed, and modified under those terms. |
| 62 | + * |
| 63 | + * This program is distributed in the hope that it will be useful, |
| 64 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 65 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 66 | + * GNU General Public License for more details. |
| 67 | + * |
| 68 | + */ |
| 69 | + |
| 70 | +#include <linux/earlysuspend.h> |
| 71 | +#include <linux/module.h> |
| 72 | +#include <linux/mutex.h> |
| 73 | +#include <linux/rtc.h> |
| 74 | +#include <linux/syscalls.h> /* sys_sync */ |
| 75 | +#include <linux/wakelock.h> |
| 76 | +#include <linux/workqueue.h> |
| 77 | + |
| 78 | +#include "power.h" |
| 79 | + |
| 80 | +enum { |
| 81 | + DEBUG_USER_STATE = 1U << 0, |
| 82 | + DEBUG_SUSPEND = 1U << 2, |
| 83 | +}; |
| 84 | +static int debug_mask = DEBUG_USER_STATE; |
| 85 | +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); |
| 86 | + |
| 87 | +static DEFINE_MUTEX(early_suspend_lock); |
| 88 | +static LIST_HEAD(early_suspend_handlers); |
| 89 | +static void early_suspend(struct work_struct *work); |
| 90 | +static void late_resume(struct work_struct *work); |
| 91 | +static DECLARE_WORK(early_suspend_work, early_suspend); |
| 92 | +static DECLARE_WORK(late_resume_work, late_resume); |
| 93 | +static DEFINE_SPINLOCK(state_lock); |
| 94 | +enum { |
| 95 | + SUSPEND_REQUESTED = 0x1, |
| 96 | + SUSPENDED = 0x2, |
| 97 | + SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED, |
| 98 | +}; |
| 99 | +static int state; |
| 100 | + |
| 101 | +void register_early_suspend(struct early_suspend *handler) |
| 102 | +{ |
| 103 | + struct list_head *pos; |
| 104 | + |
| 105 | + mutex_lock(&early_suspend_lock); |
| 106 | + list_for_each(pos, &early_suspend_handlers) { |
| 107 | + struct early_suspend *e; |
| 108 | + e = list_entry(pos, struct early_suspend, link); |
| 109 | + if (e->level > handler->level) |
| 110 | + break; |
| 111 | + } |
| 112 | + list_add_tail(&handler->link, pos); |
| 113 | + if ((state & SUSPENDED) && handler->suspend) |
| 114 | + handler->suspend(handler); |
| 115 | + mutex_unlock(&early_suspend_lock); |
| 116 | +} |
| 117 | +EXPORT_SYMBOL(register_early_suspend); |
| 118 | + |
| 119 | +void unregister_early_suspend(struct early_suspend *handler) |
| 120 | +{ |
| 121 | + mutex_lock(&early_suspend_lock); |
| 122 | + list_del(&handler->link); |
| 123 | + mutex_unlock(&early_suspend_lock); |
| 124 | +} |
| 125 | +EXPORT_SYMBOL(unregister_early_suspend); |
| 126 | + |
| 127 | +static void early_suspend(struct work_struct *work) |
| 128 | +{ |
| 129 | + struct early_suspend *pos; |
| 130 | + unsigned long irqflags; |
| 131 | + int abort = 0; |
| 132 | + |
| 133 | + mutex_lock(&early_suspend_lock); |
| 134 | + spin_lock_irqsave(&state_lock, irqflags); |
| 135 | + if (state == SUSPEND_REQUESTED) |
| 136 | + state |= SUSPENDED; |
| 137 | + else |
| 138 | + abort = 1; |
| 139 | + spin_unlock_irqrestore(&state_lock, irqflags); |
| 140 | + |
| 141 | + if (abort) { |
| 142 | + if (debug_mask & DEBUG_SUSPEND) |
| 143 | + pr_info("early_suspend: abort, state %d\n", state); |
| 144 | + mutex_unlock(&early_suspend_lock); |
| 145 | + goto abort; |
| 146 | + } |
| 147 | + |
| 148 | + if (debug_mask & DEBUG_SUSPEND) |
| 149 | + pr_info("early_suspend: call handlers\n"); |
| 150 | + list_for_each_entry(pos, &early_suspend_handlers, link) { |
| 151 | + if (pos->suspend != NULL) |
| 152 | + pos->suspend(pos); |
| 153 | + } |
| 154 | + mutex_unlock(&early_suspend_lock); |
| 155 | + |
| 156 | + if (debug_mask & DEBUG_SUSPEND) |
| 157 | + pr_info("early_suspend: sync\n"); |
| 158 | + |
| 159 | + sys_sync(); |
| 160 | +abort: |
| 161 | + spin_lock_irqsave(&state_lock, irqflags); |
| 162 | + if (state == SUSPEND_REQUESTED_AND_SUSPENDED) |
| 163 | + wake_unlock(&main_wake_lock); |
| 164 | + spin_unlock_irqrestore(&state_lock, irqflags); |
| 165 | +} |
| 166 | + |
| 167 | +static void late_resume(struct work_struct *work) |
| 168 | +{ |
| 169 | + struct early_suspend *pos; |
| 170 | + unsigned long irqflags; |
| 171 | + int abort = 0; |
| 172 | + |
| 173 | + mutex_lock(&early_suspend_lock); |
| 174 | + spin_lock_irqsave(&state_lock, irqflags); |
| 175 | + if (state == SUSPENDED) |
| 176 | + state &= ~SUSPENDED; |
| 177 | + else |
| 178 | + abort = 1; |
| 179 | + spin_unlock_irqrestore(&state_lock, irqflags); |
| 180 | + |
| 181 | + if (abort) { |
| 182 | + if (debug_mask & DEBUG_SUSPEND) |
| 183 | + pr_info("late_resume: abort, state %d\n", state); |
| 184 | + goto abort; |
| 185 | + } |
| 186 | + if (debug_mask & DEBUG_SUSPEND) |
| 187 | + pr_info("late_resume: call handlers\n"); |
| 188 | + list_for_each_entry_reverse(pos, &early_suspend_handlers, link) |
| 189 | + if (pos->resume != NULL) |
| 190 | + pos->resume(pos); |
| 191 | + if (debug_mask & DEBUG_SUSPEND) |
| 192 | + pr_info("late_resume: done\n"); |
| 193 | +abort: |
| 194 | + mutex_unlock(&early_suspend_lock); |
| 195 | +} |
| 196 | + |
| 197 | +void request_suspend_state(suspend_state_t new_state) |
| 198 | +{ |
| 199 | + unsigned long irqflags; |
| 200 | + int old_sleep; |
| 201 | + |
| 202 | + spin_lock_irqsave(&state_lock, irqflags); |
| 203 | + old_sleep = state & SUSPEND_REQUESTED; |
| 204 | + if (debug_mask & DEBUG_USER_STATE) { |
| 205 | + struct timespec ts; |
| 206 | + struct rtc_time tm; |
| 207 | + getnstimeofday(&ts); |
| 208 | + rtc_time_to_tm(ts.tv_sec, &tm); |
| 209 | + pr_info("request_suspend_state: %s (%d->%d) at %lld " |
| 210 | + "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", |
| 211 | + new_state != PM_SUSPEND_ON ? "sleep" : "wakeup", |
| 212 | + requested_suspend_state, new_state, |
| 213 | + ktime_to_ns(ktime_get()), |
| 214 | + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, |
| 215 | + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); |
| 216 | + } |
| 217 | + if (!old_sleep && new_state != PM_SUSPEND_ON) { |
| 218 | + state |= SUSPEND_REQUESTED; |
| 219 | + queue_work(suspend_work_queue, &early_suspend_work); |
| 220 | + } else if (old_sleep && new_state == PM_SUSPEND_ON) { |
| 221 | + state &= ~SUSPEND_REQUESTED; |
| 222 | + wake_lock(&main_wake_lock); |
| 223 | + queue_work(suspend_work_queue, &late_resume_work); |
| 224 | + } |
| 225 | + requested_suspend_state = new_state; |
| 226 | + spin_unlock_irqrestore(&state_lock, irqflags); |
| 227 | +} |
| 228 | + |
| 229 | +suspend_state_t get_suspend_state(void) |
| 230 | +{ |
| 231 | + return requested_suspend_state; |
| 232 | +} |
| 233 | --- a/kernel/power/power.h |
| 234 | +++ b/kernel/power/power.h |
| 235 | @@ -230,3 +230,9 @@ extern struct workqueue_struct *suspend_ |
| 236 | extern struct wake_lock main_wake_lock; |
| 237 | extern suspend_state_t requested_suspend_state; |
| 238 | #endif |
| 239 | + |
| 240 | +#ifdef CONFIG_EARLYSUSPEND |
| 241 | +/* kernel/power/earlysuspend.c */ |
| 242 | +void request_suspend_state(suspend_state_t state); |
| 243 | +suspend_state_t get_suspend_state(void); |
| 244 | +#endif |
| 245 | |