注册:函数调用
设备挂到电源管理的函数调用关系是(依次往下调用)
audio_card_init (函数类型包含__init 初始化调用)
platform_device_add
device_add
device_pm_add
list_add_tail(最直接的链表添加操作)
以音频设备注册为例,音频声卡初始化
代码目录:kernel/sound/soc/xxxx.c
static int __init audio_card_init(void)
{
int ret =0;
xxxx_snd_device = platform_device_alloc("soc-audio", -1);
if (!xxxx_snd_device) {
ret = -ENOMEM;
return ret;
}
platform_set_drvdata(xxxx_snd_device, &xxxx_snd_devdata);
xxxx_snd_devdata.dev = &xxxx_snd_device->dev;
ret = platform_device_add(xxxx_snd_device);
if (ret) {
platform_device_put(xxxx_snd_device);
}
return ret;
}
平台注册
代码目录:kernel/drivers/base/platform.c
/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
* This is part 2 of platform_device_register(), though may be called
* separately _iff_ pdev was allocated by platform_device_alloc().
*/
int platform_device_add(struct platform_device *pdev)
{
//...
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
//...
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
设备添加
代码目录:kernel/drivers/base/core.c
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
* This adds @dev to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
*
* NOTE: _Never_ directly free @dev after calling this function, even
* if it returned an error! Always use put_device() to give up your
* reference instead.
*/
int device_add(struct device *dev)
{
//...
device_pm_add(dev);
//...
}
设备添加到电源管理链表中
代码目录:kernel/drivers/base/main.c
/**
* device_pm_add - Add a device to the PM core's list of active devices.
* @dev: Device to add to the list.
*/
void device_pm_add(struct device *dev)
{
pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj));
mutex_lock(&dpm_list_mtx);
if (dev->parent) {
if (dev->parent->power.status >= DPM_SUSPENDING)
dev_warn(dev, "parent %s should not be sleeping\n",
dev_name(dev->parent));
} else if (transition_started) {
/*
* We refuse to register parentless devices while a PM
* transition is in progress in order to avoid leaving them
* unhandled down the road
*/
dev_WARN(dev, "Parentless device registered during a PM transaction\n");
}
list_add_tail(&dev->power.entry, &dpm_list);
mutex_unlock(&dpm_list_mtx);
}
应用:android休眠
1、state_store 调用 request_suspend_state,
2、然后request_suspend_state 调用early_suspend_work,完成一级休眠工作,
实际操作就是调用使用register_early_suspend注册的设备的suspend handle
比如触摸屏、按键、背光、重力感应等
3、early_suspend完成以后,
在函数末尾调用 wake_unlock(&main_wake_lock),释放掉非超时锁main_wake_lock,
如果应用程序没有不释放的锁了,就会调用到二级睡眠入口函数suspend
4、suspend调用pm_suspend,再调用enter_state,
这里才是真正二级待机的入口了
5、在函数suspend_prepare完成冻结进程成功后,就到了关于非系统设备的suspend函数,
就是suspend_devices_and_enter
6、suspend_devices_and_enter进来以后先关掉控制器,
如果开发人员,此时就不能打出log了,为调试方便可以不关闭控制器;
dpm_suspend_start(PMSG_SUSPEND)就是和本文有关的函数入口了,
它要完成的工作是关闭所有(源代码是全部)非系统设备(non-sysdev),
里面的管理就是用到前面注册添加好的链表了
7、dpm_prepare和dpm_suspend类似,
只是前者是遍历链表置状态位标志status为prepare,调用设备的prepare函数;
后者是遍历链表置状态位标志status为suspend,调用设备的suspend函数