//kernel/kernel/power/Makefile
ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG
obj-y += qos.o
obj-$(CONFIG_PM) += main.o
obj-$(CONFIG_VT_CONSOLE_SLEEP) += console.o //??
obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_SUSPEND) += suspend.o
obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o
obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \
block_io.o //这个目前没有定义,所以PM_HIBERNATION_PREPARE等还没有!!
obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o
obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o
obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
obj-$(CONFIG_SUSPEND) += wakeup_reason.o
//kernel/driver/power/Makefile
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o //
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
obj-$(CONFIG_PM_OPP) += opp.o
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
1) 内核中调用wake_lock函数,其定义在wakelock.h文件中
static inline void wake_lock(struct wake_lock *lock)
{
__pm_stay_awake(&lock->ws);
}
CONFIG_PM_WAKELOCKS使能之后,会向上层提供wake_lock节点
#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); //这个函数也是调用上面提供的__pm_stay_awake()函数!
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);
从上面的代码可以看到,wake_lock就是调用__pm_stay_awake()函数来完成阻止系统进入睡眠的功能。
__pm_stay_awake() 怎么防止系统进入syspend状态的呢??这些都是通过wakeup_source来完成的。
如果某个驱动想要阻止,允许进入睡眠,可以通过wakeup_source_init(struct wakeup_source *ws,const char *name)函数初始化一个wake source并调用__pm_stay_awake() ,或者对应地使用 __pm_relax()来阻止/允许进入睡眠。
这个wake_source在suspend流程中可以看到,在try_to_suspend()函数中会调用check wakeup_sources()来检查wake_source并决定是否进入pm_suspend()。
上面说wake_lock怎么去阻止系统进入suspend状态的,还需要解释一下Linux进入syspend状态的流程。
1)一个是/sys/power/state中写入mem或者on等进行suspend或者唤醒(/kernel/kernel/power/main.c)
这个过程需要再分析PowerManagerService(??)
2) 从/kernel/kernel/power/autosleep.c文件中的try_to_suspend()函数,这个也是PowerManagerService
从/sys/power/autosleep发来的(??)。
root@xxx:/sys/power # ls
ls
autosleep
cpufreq_max_limit
cpufreq_min_limit
cpufreq_table
pm_async
pm_freeze_timeout
state
wake_lock
wake_unlock
wakeup_count
当然这些过程都会检查wake source是否全是deactive状态。wakesource有active和deactive两种状态,当/sys/power/autosleep或者/sys/power/state写入mem等准备进入suspend的时候都会去检查wakesource链表中的所有wake source是否都处于deactive状态,是的话才会进入suspend状态。wake lock的实现也是封装了wake source实现的,用红黑树和LRU管理。wake source会在300秒之后被从wake lock的链表中移除(??)
Android中wakelock的获取像上图所示。获取wake lock的代码如下:
1) 获取wake lock
final PowerManager powerManager = (PowerManager) context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
...
mWakeLock.acquire();
mWakeLock.setReferenceCounted(false); ////使用非计时Wakelock
...
2) 释放wake lock
mWakeLock.release();
上面的PARTIAL_WAKE_LOCK似乎和内核没有什么联系,具体怎么实现的以后再看?????????。
下面是几种上层实现的锁的解释!!
newWakeLock(int flags, String tag);//取得相应层次的锁
flags参数说明:
PARTIAL_WAKE_LOCK: Screen off, keyboard light off
//保持CPU运转,允许屏幕和键盘灯有可能是关闭的
SCREEN_DIM_WAKE_LOCK: screen dim, keyboard light off
//保持CPU运转,保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK: screen bright, keyboard light off
//保持CPU运转,保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK: screen bright, keyboard bright
//保持CPU运转,保持屏幕高亮显示,键盘灯也保持亮度
ACQUIRE_CAUSES_WAKEUP: 一旦有请求锁时强制打开Screen和keyboard light
ON_AFTER_RELEASE: 在释放锁时reset activity timer
Note:
如果申请了partial wakelock,那么即使按Power键,系统也不会进Sleep,如Music播放时
如果申请了其它的wakelocks,按Power键,系统还是会进Sleep
如果Screen off timer时间到并且没有Full wake lock或者用户按了power key,那么系统状态将被切换到
NOTIFICATION,并且调用所有已经注册的g_early_suspend_handlers函数, 通常会把LCD和Backlight驱动
注册成early suspend类型,如有需要也可以把别的驱动注册成early suspend, 这样就会在第一阶段被关闭。
接下来系统会判断是否有partial wake lock acquired, 如果有则等待其释放, 在等待的过程中如果有
user activity事件发生,系统则马上回到AWAKE状态;如果没有partial wake lock acquired, 则再释放
main_wake_lock系统开机时定义的内核态唤醒锁,然后系统会马上调用函数pm_suspend关闭其它相关的驱动,
让CPU进入休眠状态(进入Linux标准的内核休眠).
3) 权限获取
要进行电源的操作需要在AndroidManifest.xml中声明该应用有设置电源管理的权限。
<uses-permissionandroid:name="android.permission.WAKE_LOCK"/>你可能还需要
<uses-permission android:name="android.permission.DEVICE_POWER"/>
以下是上面的acquire函数是怎么一步步调用到内核的wake_lock节点并获取wake_lock的步骤
mWakeLock.acquire()->PowerManager.acquireLocked()->
PowerManagerServer.acquireWakeLock()->
PowerManagerServer.acquireWakeLockInternal()->
PowerManagerServer.updatePowerStateLocked()->
PowerManagerServer.updateSuspendBlockerLocked()->
PowerManagerServer.java文件中的acquire()->
nativeAcquireSuspendBlocker()函数
然后最终调用到android/hardware/libhardware_legacy/power/power.c文件中的
acquire_wake_lock()函数
int acquire_wake_lock(int lock, const char* id)
{
initialize_fds();
// ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);
if (g_error) return g_error;
int fd;
if (lock == PARTIAL_WAKE_LOCK) {
fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
}
else {
return EINVAL;
}
return write(fd, id, strlen(id));
}
//以下是打开Linux内核向上层提供的节点的函数,可以看到打开的节点就是
/* const char * const NEW_PATHS[] = { "/sys/power/wake_lock", "/sys/power/wake_unlock", }; */
static inline void initialize_fds(void)
{
// XXX: should be this:
//pthread_once(&g_initialized, open_file_descriptors);
// XXX: not this:
if (g_initialized == 0) {
if(open_file_descriptors(NEW_PATHS) < 0)
open_file_descriptors(OLD_PATHS);
g_initialized = 1;
}
}
参考:http://www.wowotech.net/linux_kenrel/wakeup_events_framework.html
Linux内核版本3.5和Android版本4.3开始已经不再使用Early suspend。而是都换成了autosleep。
kernel断CONFIG_HAS_EARLYSUSPEND也被disable掉换成了CONFIG_PM_AUTOSLEEP。
内核的实现和上面说的wakelock等都很像,都是/kernel/kernel/power/目录下的,只是有一个叫autosleep.c的文件负责创建autosleept节点给上层。
autosleep替换early suspend之后。之前使用early suspend的设备(比如TSP),现在都只能使用autosleep机制来是先了,已经不在内核注册early suspend等等,这部分被从内核移除。
之前early suspend上的resume和suspend函数被input_dev->open和input_dev->close()所取代。调用这些内容的也从内核去掉,移到上册HAL里边。但一直没有找到在哪里调用input_dev->open和input_dev->close()。比如关掉屏幕等操作的时候,是有DisplayManagerService.java文件中的BlankUnblankInSeperateThread进程在进行input_dev->open和input_dev->close()的调用,完成类似之前early suspend的功能的。
input_dev->open()和input_dev->close都是通过input_open_device()和input_close_device()调用下来。
而input_open_device和input_close_device()就是往/sys/class/input/event*/device/enabled节点里边写1或者写0来完成的。当然,需要睡眠的输入输出设备是在InputReader.cpp文件的addDeviceLocked()函数中加的。
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
const char* deviceName = identifier.name.string();
//比如三星的设备,其触摸屏或者pen都是起来下面的名字。看到这些名字的输入输出设备就调用如下
//notifySuspendDevice()函数使能这个设备!!
if (strstr(deviceName, "sec_touchkey") || strstr(deviceName, "sec_touchscreen") ||
strstr(deviceName, "sec_e-pen")) {
const char* path = mEventHub->getDevicePath(deviceId);
if (path != NULL) {
ALOGD("reporting suspendable device %s as number %s", deviceName, path + 16);
mPolicy->notifySuspendDevice(path + 16, deviceName);
// 16 character should be excluded ("/dev/input/event")
}
}
...
}
//com_android_input_InputManagerService.cpp
void NativeInputManager::notifySuspendDevice(const char* number, const char* deviceName) {
android_server_PowerManagerService_addDevice(number, deviceName);
//[@input_fw disable suspendible device in case of sidesync
String8 path;
path.append("/sys/class/input/event");
path.append(number);
path.append("/device/enabled");
String8 name;
name.append(deviceName);
JNIEnv* env = jniEnv();
ScopedLocalRef<jstring> pathObj(env, env->NewStringUTF(path.string()));
ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(name.string()));
env->CallVoidMethod(mServiceObj,
gServiceClassInfo.getSuspendibleDevices, pathObj.get(), nameObj.get());
checkAndClearExceptionFromCallback(env, "getSuspendibleDevices");
//]
}
//上面的函数读/sys/class/input/event*/device/enabled文件并调用InputManagerService.java中的
//getSuspendibleDevices()函数保存在mSuspendibleDevices中。
//setSuspendibleDevices()函数就是mSuspendibleDevices中读取可以睡眠的输入输出设备,设置往其设备节点中的
//enable节点写值,就会调用input_dev->open函数或者input_dev->close。
//[@input_fw disable suspendible device in case of sidesync
private void getSuspendibleDevices(String path, String name) {
mSuspendibleDevices.put(name, path);
Log.d(TAG, "get suspendable device " + name + " as path " + path);
}
private boolean setSuspendibleDevices(String device, boolean enable) {
if (mSuspendibleDevices.isEmpty()) {
Log.d(TAG, "Not exist SuspendibleDevices");
return false;
}
if (device.equals("all")) {
String[] targetdevices = new String[mSuspendibleDevices.size()];
mSuspendibleDevices.keySet().toArray(targetdevices);
for(String targetdevice:targetdevices) {
sysfsWrite(mSuspendibleDevices.get(targetdevice), (enable ? 1 : 0));
Log.d(TAG, targetdevice + " is " + (enable ? "enabled" : "disabled"));
}
return true;
} else if (mSuspendibleDevices.containsKey(device)) {
sysfsWrite(mSuspendibleDevices.get(device), (enable ? 1 : 0));
Log.d(TAG, device + " is " + (enable ? "enabled" : "disabled"));
return true;
}
return false;
}
关于Early suspend:
http://blog.csdn.net/droidphone/article/details/6642081
http://blog.csdn.net/wlwl0071986/article/details/9746135
http://blog.csdn.net/wlsfling/article/details/46005409
http://blog.csdn.net/u010681466/article/details/19897851
frameworks/base/services/core/java/com/android/server/lights/LightsService.java,
frameworks/base/services/core/java/com/android/server/lights/Light.java
DisplayPowerState.java等需要管理背光和LED等的地方,都是调用setBrightness()函数来实现。
setBrightness()函数实现在LightsService.java文件中。调用顺序如下:
1. setBrightness()
[frameworks/base/services/core/java/com/android/server/lights/LightsService.java]
2. setLight_native()
[frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp]
3.set_light()[android/hardware/qcom/display/liblight/Lights.c](高通)
Lights.c文件中定义了所有LED灯的路径,有相关的驱动变更需要查看相关驱动的路径等~
char const*const RED_LED_FILE
= "/sys/class/leds/red/brightness";
char const*const GREEN_LED_FILE
= "/sys/class/leds/green/brightness";
char const*const BLUE_LED_FILE
= "/sys/class/leds/blue/brightness";
char const*const LCD_FILE
= "/sys/class/leds/lcd-backlight/brightness";
char const*const BUTTON_FILE
= "/sys/class/leds/button-backlight/brightness";
char const*const RED_BLINK_FILE
= "/sys/class/leds/red/blink";
char const*const GREEN_BLINK_FILE
= "/sys/class/leds/green/blink";
char const*const BLUE_BLINK_FILE
= "/sys/class/leds/blue/blink";