註冊:函數調用
裝置掛到電源管理的函數調用關係是(依次往下調用)
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函數