1. framework层
1.1 java层
休眠流程中上层有很多种,比如按power key进入休眠流程,还是就是settings下面的自动息屏流程。
1.1.1 power key休眠流程
当用户按下power key的时候,PhoneWindowManager类下的interceptBeforeQueueing, 在handle special key流程中,会命中KeyEvent.KEYCODE_POWER.
其业务逻辑里面就会调用到inteceptPowerKeyDown和interceptPowerKeyUp, 而休眠流程的起始条件,灭屏流程就是从这个interceptPowerKeyUp开始的。之后会就interceptPowerKeyUp展开,
但我们先来看下从key event发出后,怎么走到interceptBeforeQueueing的,这里主要看下framework层的获得key event事件后响应流程,至于key event事件怎么分发的和怎么获得的 需要InuptDispatcher(inputFlinger)部分来分析,之后有时间再补充。
com_android_server_input_InputManagerService.cpp在收到InputDispatcher分发的出来PowerKey 事件之后,就会走到NativeInputManager::dispatchUnhandledKey方法,
这里方法里通过回调的机制回调到InputManagerService.java下面的native方法dispatchUnhandleKey,注意这个方法是回调方法。然后在调到InputManagerCallback下面的dispatchUnhandleKey,
然后再调到PhoneWindowManager下面的dispatchUnhandleKey方法, 然后调到interceptFallback方法,最终从这里面调到interceptBeforeQueueing这个方法。
(类似的,ARN机制里面关于framework的一部分流程也是通过InputManagerService实现的,从native类com_android_server_input_InputManagerService.cpp的notifyANR通过反射开始回调java的方法InputManagerService类下的notifyANR方法,当然通信机制还是Binder了)。
interceptPowerKeyUp流程展开,这里方法里会调用powerPress这个方法,源码如下
private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
...
if (!handled) {
...
// Figure out how to handle the key now that it has been released.
mPowerKeyPressCounter += 1;
final int maxCount = getMaxMultiPressPowerCount();
final long eventTime = event.getDownTime();
if (mPowerKeyPressCounter < maxCount) {
// This could be a multi-press. Wait a little bit longer to confirm.
// Continue holding the wake lock.
Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout());
return;
}
// No other actions. Handle it immediately.
powerPress(eventTime, interactive, mPowerKeyPressCounter);/*这里开始灭屏流程*/
}
// Done. Reset our state.
finishPowerKeyPress();
}
powerPress主要实现的是根据power key按键次数来决定走什么流程,当按1次的时候走的是goToSleepFromPowerButton(), 其中SHORT_PRESS_POWER_GO_TO_SLEEP走doze流程,
SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP走的是直接sleep的流程,还有按power key唤醒到到home的流程也走这里。
private void powerPress(long eventTime, boolean interactive, int count) {
...
if (count == 2) {
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (interactive && !mBeganFromNonInteractive) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP:
goToSleepFromPowerButton(eventTime, 0);//走DOZE流程
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);//直接走sleep流程
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
if (goToSleepFromPowerButton(eventTime,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
launchHomeFromHotKey(DEFAULT_DISPLAY);
}
break;
case SHORT_PRESS_POWER_GO_HOME:
shortPressPowerGoHome();
break;
case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
if (mDismissImeOnBackKeyPressed) {
if (mInputMethodManagerInternal == null) {
mInputMethodManagerInternal =
LocalServices.getService(InputMethodManagerInternal.class);
}
if (mInputMethodManagerInternal != null) {
mInputMethodManagerInternal.hideCurrentInputMethod();
}
} else {
shortPressPowerGoHome();
}
break;
}
}
}
}
接下来就通过2段代码调了PMS的goToSleep方法
/**
* Sends the device to sleep as a result of a power button press.
*
* @return True if the was device was sent to sleep, false if sleep was suppressed.
*/
private boolean goToSleepFromPowerButton(long eventTime, int flags) {
// Before we actually go to sleep, we check the last wakeup reason.
// If the device very recently woke up from a gesture (like user lifting their device)
// then ignore the sleep instruction. This is because users have developed
// a tendency to hit the power button immediately when they pick up their device, and we
// don\'t want to put the device back to sleep in those cases.
final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup();
if (lastWakeUp != null && lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE) {
final int gestureDelayMillis = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
final long now = SystemClock.uptimeMillis();
if (mPowerButtonSuppressionDelayMillis > 0
&& (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {
Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: "
+ (now - lastWakeUp.wakeTime) + "ms");
return false;
}
}
goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
return true;
}
private void goToSleep(long eventTime, int reason, int flags) {
mRequestedOrGoingToSleep = true;
mPowerManager.goToSleep(eventTime, reason, flags);
}
//PowerManager.java
public void goToSleep(long time, int reason, int flags) {
try {
mService.goToSleep(time, reason, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
goToSleepInternal(eventTime, reason, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
synchronized (mLock) {
if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
updatePowerStateLocked();
}
}
}
到此,可以看出PowerManager.java下的updatePowerStateLocked是一个统一的接口,也PMS的核心方法,也就是无论是按power key休眠还是自动息屏休眠,都会走到这里。
之后的流程本文后面统一分析,先看自动息屏流程。
1.1.2 自动息屏流程
为什么会自动息屏,简单的理解就是用户长时间内没有跟设备进行交互,然后触发了自动息屏流程。
自动休眠流程从PMS初始化开始就执行这个核心方法,这个方法又会去调updateUserActivitySummaryLocked,正是这个方法去计算用户没有响应的时间,当没有相应的时间
达到用户设置的息屏时间时,就设置mUserActivitySummary 为USER_ACTIVITY_SCREEN_DIM,再设置成USER_ACTIVITY_SCREEN_DREAM,最后设置成0。mUserActivitySummary为0时就会触发updatePowerStateLocked里的休眠流程了,也就是updateSuspendBlockerLocked。
额外提一下,与power key 的上报事件流程类似, userAcivity也是类似从底层开始接受用户跟设备的交互事件,这里直接从JAVA层开始讲,PMS有这样一个native方法userActivityFromNative。
1.1.3 updateSuspendBlockerLocked
/**
* Updates the suspend blocker that keeps the CPU alive.
*
* This function must have no other side-effects.
*/
private void updateSuspendBlockerLocked() {
// Disable auto-suspend if needed.
// FIXME We should consider just leaving auto-suspend enabled forever since
// we already hold the necessary wakelocks.
if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
setHalAutoSuspendModeLocked(false);
}
// First acquire suspend blockers if needed.
if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.acquire();
mHoldingWakeLockSuspendBlocker = true;
}
if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
mDisplaySuspendBlocker.acquire();
mHoldingDisplaySuspendBlocker = true;
}
// Inform the power HAL about interactive mode.
// Although we could set interactive strictly based on the wakefulness
// as reported by isInteractive(), it is actually more desirable to track
// the display policy state instead so that the interactive state observed
// by the HAL more accurately tracks transitions between AWAKE and DOZING.
// Refer to getDesiredScreenPolicyLocked() for details.
if (mDecoupleHalInteractiveModeFromDisplayConfig) {
// When becoming non-interactive, we want to defer sending this signal
// until the display is actually ready so that all transitions have
// completed. This is probably a good sign that things have gotten
// too tangled over here...
if (interactive || mDisplayReady) {
setHalInteractiveModeLocked(interactive);
}
}
// Then release suspend blockers if needed.
if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.release();
mHoldingWakeLockSuspendBlocker = false;
}
if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
mDisplaySuspendBlocker.release();
mHoldingDisplaySuspendBlocker = false;
}
// Enable auto-suspend if needed.
if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
setHalAutoSuspendModeLocked(true);
}
}
/** Wrapper for PowerManager.nativeSetAutoSuspend */
public void nativeSetAutoSuspend(boolean enable) {
PowerManagerService.nativeSetAutoSuspend(enable);
}
setHalAutoSuspendModeLocked 最终调用到了native方法nativeSetAutoSuspend。
1.2 native层
从native层开始,不管是power key键还是自动息屏休眠流程,都走的是nativeSetAutoSuspend, 别误以为autosuspend就是自动息屏休眠。autosuspend是android对linux源生suspend机制的补充。
从源码里可以看到从java传下来的enable参数是true的时候,它就去enblesuspend,如果是false,就去申请锁。同时这个锁会在enable为true的时候去释放,所以不影响休眠流程。
static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
if (enable) {
enableAutoSuspend();
}
} else {
disableAutoSuspend();
}
}
}
void enableAutoSuspend() {
static bool enabled = false;
if (!enabled) {
sp<ISuspendControlService> suspendControl = getSuspendControl();
suspendControl->enableAutosuspend(&enabled);
}
{
std::lock_guard<std::mutex> lock(gSuspendMutex);
if (gSuspendBlocker) {
gSuspendBlocker->release();
gSuspendBlocker.clear();
}
}
}
void disableAutoSuspend() {
std::lock_guard<std::mutex> lock(gSuspendMutex);
if (!gSuspendBlocker) {
sp<ISystemSuspend> suspendHal = getSuspendHal();
gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL,
"PowerManager.SuspendLockout");
}
}
接下来再来看源码,这个suspendControl->enableAutosuspend(&enabled); 是调用哪里的方法
void SuspendControlService::setSuspendService(const wp<SystemSuspend>& suspend) {
mSuspend = suspend;
}
binder::Status SuspendControlService::enableAutosuspend(bool* _aidl_return) {
const auto suspendService = mSuspend.promote();
return retOk(suspendService != nullptr && suspendService->enableAutosuspend(), _aidl_return);
}
bool SystemSuspend::enableAutosuspend() {
static bool initialized = false;
if (initialized) {
LOG(ERROR) << "Autosuspend already started.";
return false;
}
initAutosuspend();
initialized = true;
return true;
}
所以最终调到了SystemSuspend.cpp下的enableAutosuspend函数。
来详细看下initAutosuspend方法
void SystemSuspend::initAutosuspend() {
std::thread autosuspendThread([this] {
while (true) {
std::this_thread::sleep_for(mSleepTime);
lseek(mWakeupCountFd, 0, SEEK_SET);
const string wakeupCount = readFd(mWakeupCountFd);
auto counterLock = std::unique_lock(mCounterLock);
mCounterCondVar.wait(counterLock, [this] { return mSuspendCounter == 0; });
// The mutex is locked and *MUST* remain locked until we write to /sys/power/state.
// Otherwise, a WakeLock might be acquired after we check mSuspendCounter and before we
// write to /sys/power/state.
if (!WriteStringToFd(wakeupCount, mWakeupCountFd)) {
PLOG(VERBOSE) << "error writing from /sys/power/wakeup_count";
continue;
}
bool success = WriteStringToFd(kSleepState, mStateFd);
counterLock.unlock();
mControlService->notifyWakeup(success);
updateSleepTime(success);
}
});
autosuspendThread.detach();
LOG(INFO) << "automatic system suspend enabled";
}
实际上就是去写了2个节点文件,"/sys/power/wakeup_count", “/sys/power/state”, 当echo “mem” > /sys/power/state 且 “/sys/power/wakeup_count” 等于0的时候,系统就会进入suspend。
所以android本身就做了一些wrap, 只要上层能够实现上面这2个条件,系统就会进入suspend状态。而这些wrap就是kernel层的休眠机制,第2章接着讲。
在讲kernel休眠机制之前,先来总结下native层的框架,native层也采用RPC通信机制,也用到aidl接口。
实际上,com_android_server_power_PowerManagerService这个是这个机制中的客户端,而SuspendControlService是这个机制的服务端,他是对ISuspendControlService.aidl接口的具体实现。
这个服务端中又调用到了一个HAL的具体实现类SystemSuspend.cpp,它可以理解为是硬件的一个具体模块类。大致调用关系图如下
2. kernel层
2.1 kernel休眠机制简介
Android在Linux内核原有的睡眠唤醒模块上基础上,主要增加了下面三个机制:
Wake _Lock 唤醒锁机制;
Early _Suspend 预挂起机制;
Late _Resume 迟唤醒机制;
其基本原理如下:
当启动一个应用程序的时候,它都可以申请一个wake_lock唤醒锁,每当申请成功之后都会在内核中注册一下(通知系统内核,现在已经有锁被申请),当应用程序在某种情况下释放wake_lock的时候,会注销之前所申请的wake_lock。特别要注意的是:只要是系统中有一个wake_lock的时候,系统此时都不能进行睡眠。但此时各个模块可以进行early_suspend。当系统中所有的wake_lock都被释放之后,系统就会进入真正的kernel的睡眠状态。在系统启动的时候会创建一个主唤醒锁main_wake_lock,该锁是内核初始化并持有的一个WAKE_LOCK_SUSPEND属性的非限时唤醒锁。因此,系统正常工作时,将始终因为该锁被内核持有而无法进入睡眠状态。也就是说在不添加新锁的情况下,只需将main_wake_lock 解锁,系统即可进入睡眠状态。
结构图如下:
在标准Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务;(2)调用注册的设备的suspend的回调函数,其调用顺序是按照驱动加载时的注册顺序。(3)休眠核心设备和使CPU进入休眠态冻结进程是内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文。这个动作在android中也是有的。
备注:在andoird 早些时间的之前这些写节点操作是libsuspend库中完成的,具体目录如下:
system/core/libsuspend/autosuspend.c
system/core/libsuspend/autosuspend_wakeup_count.c
2.2 休眠流程分析
讲流程之前,先将kernel节点文件的创建。
2.2.1 /sys/power/state的创建
节点文件的创建在kernel/kernel/power/main.c中实现,
1)sys/power/state节点生成
#define power_attr(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = __stringify(_name), \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
power_attr(state);
static struct attribute * g[] = {
&state_attr.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = g,
};
static int __init pm_init(void)
{
power_kobj = kobject_create_and_add("power", NULL);
if (!power_kobj)
return -ENOMEM;
error = sysfs_create_group(power_kobj, &attr_group);
}
core_initcall(pm_init);
2.2.2 /sys/power/wakeup_count的读写
这部分内容是用来记录和读取wakeup_count的次数,也在kernel/kernel/power/main.c中实现,后续详细补充。
2.2.3 kernel流程
在framework层已经执行到写"/sys/power/state"这个动作,那就会触发kernel里面的逻辑,也就是节点文件store逻辑。
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n)
{
state = decode_state(buf, n);//将"mem"转换成对应的数字
error = pm_suspend(state);//这里才是真正开启kernel的休眠流程
}
int pm_suspend(suspend_state_t state)
{
pm_suspend_marker("entry");
error = enter_state(state);
pm_suspend_marker("exit");
}
static void pm_suspend_marker(char *annotation)
{
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}
static int enter_state(suspend_state_t state)
{
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n");
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
error = suspend_prepare(state);
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
pm_restrict_gfp_mask();
error = suspend_devices_and_enter(state);
pm_restore_gfp_mask();
Finish:
pr_debug("PM: Finishing wakeup.\n");
suspend_finish();
}
//在suspend_prepare的时候去 冻结cpu的中的任务
static int suspend_prepare(suspend_state_t state)
{
if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter)) //kernel/drivers/cpuidle/lpm-levels.c suspend_set_ops(&lpm_suspend_ops);
return -EPERM;
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
error = suspend_freeze_processes();
pm_notifier_call_chain(PM_POST_SUSPEND);
return error;
}
static inline int suspend_freeze_processes(void)
{
error = freeze_processes();
error = freeze_kernel_threads();
}
//然后去suspend一些外设,最后走suspend_enter流程。
int suspend_devices_and_enter(suspend_state_t state)
{
int error;
bool wakeup = false;
if (need_suspend_ops(state) && !suspend_ops)
return -ENOSYS;
if (need_suspend_ops(state) && suspend_ops->begin) {
error = suspend_ops->begin(state);//lpm相关的
if (error)
goto Close;
}
suspend_console();
error = dpm_suspend_start(PMSG_SUSPEND);//dpm相关的
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
do {
error = suspend_enter(state, &wakeup); //正常休眠会停在该函数,如果唤醒了,则走下面的流程
} while (!error && !wakeup && need_suspend_ops(state)
&& suspend_ops->suspend_again && suspend_ops->suspend_again());
Resume_devices:
dpm_resume_end(PMSG_RESUME);//dpm相关的
resume_console();
Close:
if (need_suspend_ops(state) && suspend_ops->end)
suspend_ops->end();//lpm相关的
return error;
Recover_platform:
if (need_suspend_ops(state) && suspend_ops->recover)
suspend_ops->recover();//lpm相关的
goto Resume_devices;
}
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
error = dpm_suspend_end(PMSG_SUSPEND);//dpm相关的
error = disable_nonboot_cpus();
arch_suspend_disable_irqs();
error = syscore_suspend();
if (!error) {
*wakeup = pm_wakeup_pending();
if (!(suspend_test(TEST_CORE) || *wakeup)) {
error = suspend_ops->enter(state);//停在这..........
events_check_enabled = false;
}
syscore_resume();
}
arch_suspend_enable_irqs();
Enable_cpus:
enable_nonboot_cpus();
Platform_wake:
if (need_suspend_ops(state) && suspend_ops->wake)
suspend_ops->wake();//lpm相关的
dpm_resume_start(PMSG_RESUME);//dpm相关的
Platform_finish:
if (need_suspend_ops(state) && suspend_ops->finish)
suspend_ops->finish(); //lpm相关的
return error;
}
//这2个函数都会走到dpm里面的一些电源管理函数的逻辑和drive的里面的lpm相关的逻辑,
int dpm_suspend_start(pm_message_t state)
{
dpm_prepare(state); //prepare dpm_list
dpm_suspend(state); //suspend dpm_prepared_list
}
int dpm_suspend_end(pm_message_t state)
{
dpm_suspend_late(state); //suspend_late dpm_suspended_list
dpm_suspend_noirq(state); //suspend_noirq dpm_late_early_list
}
void dpm_resume_start(pm_message_t state)
{
dpm_resume_noirq(state); //resume_noirq
dpm_resume_early(state); //resume_early
}
void dpm_resume_end(pm_message_t state)
{
dpm_resume(state); //resume
dpm_complete(state);//complete
}
struct dev_pm_ops {
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
如
static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
.suspend = goodix_ts_suspend,
.resume = goodix_ts_resume,
};
类dpm函数的作用是按顺序执行相关的电源函数.
休眠prepare—>suspend—>suspend_late—>suspend_noirq
唤醒resume_noirq—>resume_early—>resume—>complete
//而且让cpu休眠的流程在cpuidle的驱动中实现,也就是lpm, kernel/drivers/cpuidle/lpm-levels.c。
static int lpm_probe(struct platform_device *pdev)
{
suspend_set_ops(&lpm_suspend_ops);
}
static const struct platform_suspend_ops lpm_suspend_ops = {
.enter = lpm_suspend_enter,
.valid = suspend_valid_only_mem,
.prepare_late = lpm_suspend_prepare,
.wake = lpm_suspend_wake,
};
kernel/kernel/power/suspend.c
void suspend_set_ops(const struct platform_suspend_ops *ops)
{
suspend_ops = ops;
}