1. 程式人生 > >Android休眠喚醒驅動流程分析

Android休眠喚醒驅動流程分析

在wakelock中,有2個地方可以讓系統從early_suspend進入suspend狀態。分別是:
  • 在wake_unlock中,解鎖之後,若沒有其他的wakelock,則進入suspend。
  • 在超時鎖的定時器超時後,定時器的回撥函式,會判斷有沒有其他的wakelock,若沒有,則進入suspend。

3.5 suspend()

static void suspend(struct work_struct *work)
{
	int ret;
	int entry_event_num;
	struct timespec ts_entry, ts_exit;
//如果有不是自動到期的lock還沒有unlock或者自動到期的lock還沒有到期
	if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
		if (debug_mask & DEBUG_SUSPEND)
			pr_info("suspend: abort suspend\n");
		return;
	}

	entry_event_num = current_event_num;
	if (debug_mask & DEBUG_SUSPEND)
		pr_info("suspend: sys_sync...");
	sys_sync();
	if (debug_mask & DEBUG_SUSPEND)
		pr_info("done.\n");
	if (debug_mask & DEBUG_SUSPEND)
		pr_info("suspend: enter suspend\n");
	getnstimeofday(&ts_entry);
	ret = pm_suspend(requested_suspend_state);
	getnstimeofday(&ts_exit);

	if (debug_mask & DEBUG_EXIT_SUSPEND) {
		struct rtc_time tm;
		rtc_time_to_tm(ts_exit.tv_sec, &tm);
		pr_info("suspend: exit suspend, ret = %d "
			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
			tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);
	}

	if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {
		++suspend_short_count;

		if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {
			suspend_backoff();
			suspend_short_count = 0;
		}
	} else {
		suspend_short_count = 0;
	}

	if (current_event_num == entry_event_num) {
		if (debug_mask & DEBUG_SUSPEND)
			pr_info("suspend: pm_suspend returned with no event\n");
		
	}
	wake_lock_timeout(&unknown_wakeup,2* HZ);
}
state_store()呼叫request_suspend_state(),滿足休眠狀態時,呼叫queue_work(suspend_work_queue,&early_suspend_work),呼叫了early_suspend(),然後在其中通過wake_unlock()啟動了expire_timer定時器,當定時時間到了,則執行expire_wake_locks,將suspend_work加入到佇列中,分析到這裡就可以知道了early_suspend_work和suspend_work這兩個佇列的先後順序了。

3.6 pm_suspend()

int pm_suspend(suspend_state_t state)
{
	int error;

	if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
		return -EINVAL;

	pm_suspend_marker("entry");
	error = enter_state(state);
	if (error) {
		suspend_stats.fail++;
		dpm_save_failed_errno(error);
	} else {
		suspend_stats.success++;
	}
	pm_suspend_marker("exit");
	return error;
}
static int enter_state(suspend_state_t state)
{
	int error;

	if (!valid_state(state))
		return -ENODEV;

	if (!mutex_trylock(&pm_mutex))
		return -EBUSY;

	if (state == PM_SUSPEND_FREEZE)
		freeze_begin();

	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);
	if (error)
		goto Unlock;

	if (suspend_test(TEST_FREEZER))
		goto Finish;

	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();
 Unlock:
	mutex_unlock(&pm_mutex);
	return error;
}
suspend()呼叫了pm_suspend(),通過判斷當前的狀態,選擇enter_state(),在enter_state()中,經過了suspend_prepare(),suspend_test()和suspend_device_and_enter(),在suspend_device_and_enter()中呼叫了device_suspend()來儲存狀態和結束系統的裝置,到了dpm_suspend()中結束所有的device。
3.6.1 suspend_prepare()
static int suspend_prepare(suspend_state_t state)
{
	int error;
//平臺相關
	if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))
		return -EPERM;

	pm_prepare_console();

	error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
	if (error)
		goto Finish;
//凍結程序
	error = suspend_freeze_processes();
	if (!error)
		return 0;

	suspend_stats.failed_freeze++;
	dpm_save_failed_step(SUSPEND_FREEZE);
 Finish:
	pm_notifier_call_chain(PM_POST_SUSPEND);
	pm_restore_console();
	return error;
}
在suspend_prepare()函式中,先通過pm_prepare_console()給suspend分配一個虛擬終端來輸出資訊,再廣播一個系統進入suspend的通報,關閉使用者態的helper程序,然後呼叫suspend_freeze_processes()來凍結程序,最後會嘗試釋放一些記憶體。
在suspend_freeze_processes()函式中呼叫了freeze_processes()函式,而freeze_processes()函式中又呼叫了try_to_freeze_tasks()來完成凍結任務。在凍結過程中,會判斷當前程序是否新增wakeup event,若有,則凍結失敗,函式會放棄凍結。
到現在,所有的程序(也包括workqueue/kthread) 都已經停止了,核心態程序有可能在停止的時候握有一些訊號量,所以如果這時候在外設裡面去解鎖這個訊號量有可能會發生死鎖, 所以在外設suspend()函式裡面作lock/unlock鎖要非常小心,建議不要在外設的suspend()裡面等待鎖。而且suspend的過程中,有一些log是無法輸出的,所以一旦出現問題,非常難除錯。
3.6.2 suspend_devices_and_enter()
int suspend_devices_and_enter(suspend_state_t state)
{
	int error;
	bool wakeup = false;

	if (need_suspend_ops(state) && !suspend_ops)
		return -ENOSYS;

	trace_machine_suspend(state);
//沒定義begin()略過
	if (need_suspend_ops(state) && suspend_ops->begin) {
		error = suspend_ops->begin(state);
		if (error)
			goto Close;
	}
	suspend_console();
	ftrace_stop();
	suspend_test_start();
	error = dpm_suspend_start(PMSG_SUSPEND);//device suspend
	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:
	suspend_test_start();
	dpm_resume_end(PMSG_RESUME);
	suspend_test_finish("resume devices");
	ftrace_start();
	resume_console();
 Close:
	if (need_suspend_ops(state) && suspend_ops->end)
		suspend_ops->end();
	trace_machine_suspend(PWR_EVENT_EXIT);
	return error;

 Recover_platform:
	if (need_suspend_ops(state) && suspend_ops->recover)
		suspend_ops->recover();
	goto Resume_devices;
}
suspend_devices_and_enter()函式讓外設進入休眠。該函式中,首先休眠串列埠(之後不能再顯示log,解決方法為在kernel配置選項的cmd_line中,新增”no_console_suspend”選項),再通過dpm_suspend_start()->dpm_suspend()->device_suspend()函式呼叫各驅動的suspend函式,如果是非同步suspend會呼叫async_suspend()->device_suspend()。
3.6.2.1 dpm_suspend_start()
int dpm_suspend_start(pm_message_t state)
{
	int error;
	error = dpm_prepare(state);
	if (error) {
		suspend_stats.failed_prepare++;
		dpm_save_failed_step(SUSPEND_PREPARE);
	} else
		error = dpm_suspend(state);
	return error;
}
int dpm_prepare(pm_message_t state)
{
	int error = 0;

	might_sleep();

	mutex_lock(&dpm_list_mtx);
	while (!list_empty(&dpm_list)) {
		struct device *dev = to_device(dpm_list.next);

		get_device(dev);
		mutex_unlock(&dpm_list_mtx);

//這裡會呼叫 dev->XXXX->prepare(),callback()的選擇順序同dpm_suspend()
		error = device_prepare(dev, state);

		mutex_lock(&dpm_list_mtx);
		if (error) {
			if (error == -EAGAIN) {
				put_device(dev);
				error = 0;
				continue;
			}
			printk(KERN_INFO "PM: Device %s not prepared "
				"for power transition: code %d\n",
				dev_name(dev), error);
			put_device(dev);
			break;
		}
		dev->power.is_prepared = true;
//dpm_list轉移到dpm_prepared_list,新增到結尾,兩個list順序一致
		if (!list_empty(&dev->power.entry))
			list_move_tail(&dev->power.entry, &dpm_prepared_list);
		put_device(dev);
	}
	mutex_unlock(&dpm_list_mtx);
	return error;
}
int dpm_suspend(pm_message_t state)
{
	ktime_t starttime = ktime_get();
	int error = 0;

	might_sleep();

	mutex_lock(&dpm_list_mtx);
	pm_transition = state;
	async_error = 0;
	while (!list_empty(&dpm_prepared_list)) {
		struct device *dev = to_device(dpm_prepared_list.prev);

		get_device(dev);
		mutex_unlock(&dpm_list_mtx);
//device suspend
		error = device_suspend(dev);

		mutex_lock(&dpm_list_mtx);
		if (error) {
			pm_dev_err(dev, state, "", error);
			dpm_save_failed_dev(dev_name(dev));
			put_device(dev);
			break;
		}
		if (!list_empty(&dev->power.entry))
//dpm_prepared_list轉移到dpm_suspended_list,新增到list頭,兩個list順序相反
			list_move(&dev->power.entry, &dpm_suspended_list);
		put_device(dev);
		if (async_error)
			break;
	}
	mutex_unlock(&dpm_list_mtx);
	async_synchronize_full();
	if (!error)
		error = async_error;
	if (error) {
		suspend_stats.failed_suspend++;
		dpm_save_failed_step(SUSPEND_SUSPEND);
	} else
		dpm_show_time(starttime, state, NULL);
	return error;
}

suspend callback的選擇順序:
dev->pm_domain->ops.suspend
dev->type->pm->suspend
dev->class->pm->suspend
dev->class->suspend
dev->bus->pm->suspend
dev->bus->suspend
dev->driver->pm->suspend
例如:[ [email protected]] suspend input1+ @ 865, parent: 1-0040
一般這個parent 為none,因為程式碼中
input_device->dev.parent = &client->dev;
所以找了一個parent。
input_allocate_device()->
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
input_register_device()->
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);

static struct device_type input_dev_type = {
	.groups		= input_dev_attr_groups,
	.release	= input_dev_release,
	.uevent		= input_dev_uevent,
#ifdef CONFIG_PM
	.pm		= &input_dev_pm_ops,
#endif
};
static const struct dev_pm_ops input_dev_pm_ops = {
	.suspend	= input_dev_suspend,
	.resume		= input_dev_resume,
	.poweroff	= input_dev_suspend,
	.restore	= input_dev_resume,
};
static int input_dev_suspend(struct device *dev)
{
	struct input_dev *input_dev = to_input_dev(dev);

	mutex_lock(&input_dev->mutex);

	if (input_dev->users)
		input_dev_toggle(input_dev, false);

	mutex_unlock(&input_dev->mutex);

	return 0;
}
再例如:[ [email protected]] suspend 1-0040+ @ 865, parent: i2c-1
i2c_new_device()->
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);

/* For 10-bit clients, add an arbitrary offset to avoid collisions */
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)
? 0xa000 : 0));
static struct device_type i2c_client_type = {
	.groups		= i2c_dev_attr_groups,
	.uevent		= i2c_device_uevent,
	.release	= i2c_client_dev_release,
};
沒有定義dev_pm_ops,繼續找i2c_bus_type。
struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
	.pm		= &i2c_device_pm_ops,
};
static const struct dev_pm_ops i2c_device_pm_ops = {
	.suspend = i2c_device_pm_suspend,
	.resume = i2c_device_pm_resume,
	.freeze = i2c_device_pm_freeze,
	.thaw = i2c_device_pm_thaw,
	.poweroff = i2c_device_pm_poweroff,
	.restore = i2c_device_pm_restore,
	SET_RUNTIME_PM_OPS(
		pm_generic_runtime_suspend,
		pm_generic_runtime_resume,
		pm_generic_runtime_idle
	)
};
static int i2c_device_pm_suspend(struct device *dev)
{
	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
//如果device對應的driver定義了pm,pm->suspend存在,呼叫pm->suspend(dev)
	if (pm)
		return pm_generic_suspend(dev);
//如果沒有定義pm,如果driver->suspend存在,呼叫driver->suspend(client, mesg)
	else
		return i2c_legacy_suspend(dev, PMSG_SUSPEND);
}
如果driver->pm定義了,就不會執行driver->suspend了。
再例如:[ [email protected]] suspend adc_key.10+ @ 865, parent: platform
platform_device_add()->
pdev->dev.bus = &platform_bus_type;
沒有定義pdev->dev.type。
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
static const struct dev_pm_ops platform_dev_pm_ops = {
	.runtime_suspend = pm_generic_runtime_suspend,
	.runtime_resume = pm_generic_runtime_resume,
	.runtime_idle = pm_generic_runtime_idle,
	USE_PLATFORM_PM_SLEEP_OPS
};
#define USE_PLATFORM_PM_SLEEP_OPS \
	.suspend = platform_pm_suspend, \
	.resume = platform_pm_resume, \
	.freeze = platform_pm_freeze, \
	.thaw = platform_pm_thaw, \
	.poweroff = platform_pm_poweroff, \
	.restore = platform_pm_restore,
int platform_pm_suspend(struct device *dev)
{
	struct device_driver *drv = dev->driver;
	int ret = 0;

	if (!drv)
		return 0;

	if (drv->pm) {
		if (drv->pm->suspend)
			ret = drv->pm->suspend(dev);
	} else {
		ret = platform_legacy_suspend(dev, PMSG_SUSPEND);
	}

	return ret;
}
同樣,如果driver->pm定義了,就不會執行driver->suspend了。
3.6.2.2 suspend_enter()
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
	int error;
//平臺相關
	if (need_suspend_ops(state) && suspend_ops->prepare) {
		error = suspend_ops->prepare();
		if (error)
			goto Platform_finish;
	}
// Execute "late" and "noirq" device suspend callbacks.
	error = dpm_suspend_end(PMSG_SUSPEND);
	if (error) {
		printk(KERN_ERR "PM: Some devices failed to power down\n");
		goto Platform_finish;
	}

	if (need_suspend_ops(state) && suspend_ops->prepare_late) {
		error = suspend_ops->prepare_late();
		if (error)
			goto Platform_wake;
	}

	if (suspend_test(TEST_PLATFORM))
		goto Platform_wake;

	/*
	 * PM_SUSPEND_FREEZE equals
	 * frozen processes + suspended devices + idle processors.
	 * Thus we should invoke freeze_enter() soon after
	 * all the devices are suspended.
	 */
	if (state == PM_SUSPEND_FREEZE) {
		freeze_enter();
		goto Platform_wake;
	}

	error = disable_nonboot_cpus();
	if (error || suspend_test(TEST_CPUS))
		goto Enable_cpus;
//關中斷
	arch_suspend_disable_irqs();
	BUG_ON(!irqs_disabled());

	error = syscore_suspend();
	if (!error) {
		*wakeup = pm_wakeup_pending();
		if (!(suspend_test(TEST_CORE) || *wakeup)) {
// enter(state)使CPU進入省電狀態,整個休眠過程完成,程式碼停止
			error = suspend_ops->enter(state);
			events_check_enabled = false;
		}
		syscore_resume();
	}

	arch_suspend_enable_irqs();
	BUG_ON(irqs_disabled());

 Enable_cpus:
	enable_nonboot_cpus();

 Platform_wake:
	if (need_suspend_ops(state) && suspend_ops->wake)
		suspend_ops->wake();

	dpm_resume_start(PMSG_RESUME);

 Platform_finish:
	if (need_suspend_ops(state) && suspend_ops->finish)
		suspend_ops->finish();

	return error;
}
suspend:
state_store()->request_suspend_state()->early_suspend()->wake_unlock()->expire_wake_locks()->suspend()->pm_suspend()->enter_state()->suspend_devices_and_enter()->suspend_enter()
resum:
suspend_devices_and_enter()->dpm_resume_end()->dpm_resume()->device_resume()

3.7 enter_state()->suspend_devices_and_enter()->dpm_resume_end()

void dpm_resume_end(pm_message_t state)
{
	dpm_resume(state);
	dpm_complete(state);
}
3.7.1 dpm_resume()
void dpm_resume(pm_message_t state)
{
	struct device *dev;
	ktime_t starttime = ktime_get();

	might_sleep();

	mutex_lock(&dpm_list_mtx);
	pm_transition = state;
	async_error = 0;
//非同步resum
	list_for_each_entry(dev, &dpm_suspended_list, power.entry) {
		INIT_COMPLETION(dev->power.completion);
		if (is_async(dev)) {
			get_device(dev);
			async_schedule(async_resume, dev);
		}
	}
//resum的list和suspend是相反的
	while (!list_empty(&dpm_suspended_list)) {
		dev = to_device(dpm_suspended_list.next);
		get_device(dev);
		if (!is_async(dev)) {
			int error;

			mutex_unlock(&dpm_list_mtx);
//callback選擇順序同suspend
			error = device_resume(dev, state, false);
			if (error) {
				suspend_stats.failed_resume++;
				dpm_save_failed_step(SUSPEND_RESUME);
				dpm_save_failed_dev(dev_name(dev));
				pm_dev_err(dev, state, "", error);
			}

			mutex_lock(&dpm_list_mtx);
		}
		if (!list_empty(&dev->power.entry))
			list_move_tail(&dev->power.entry, &dpm_prepared_list);
		put_device(dev);
	}
	mutex_unlock(&dpm_list_mtx);
	async_synchronize_full();
	dpm_show_time(starttime, state, NULL);
}
無論是同步resum,還是非同步resum都會呼叫device_resume();
3.7.2 dpm_complete()
void dpm_complete(pm_message_t state)
{
	struct list_head list;

	might_sleep();

	INIT_LIST_HEAD(&list);
	mutex_lock(&dpm_list_mtx);
//包括新增加的device 
	while (!list_empty(&dpm_prepared_list)) {
		struct device *dev = to_device(dpm_prepared_list.prev);

		get_device(dev);
		dev->power.is_prepared = false;
		list_move(&dev->power.entry, &list);
		mutex_unlock(&dpm_list_mtx);
//執行callbacks
		device_complete(dev, state);

		mutex_lock(&dpm_list_mtx);
		put_device(dev);
	}
	list_splice(&list, &dpm_list);
	mutex_unlock(&dpm_list_mtx);
}
睡眠喚醒過程中的一些list:
LIST_HEAD(dpm_list);//extern
static LIST_HEAD(dpm_prepared_list);
static LIST_HEAD(dpm_suspended_list);
static LIST_HEAD(dpm_late_early_list);//略過
static LIST_HEAD(dpm_noirq_list);//略過
device_initialize()->device_pm_init()->device_pm_sleep_init()->INIT_LIST_HEAD(&dev->power.entry)
device初始化時,初始化&dev->power.entry。
device_add()->device_pm_add()->list_add_tail(&dev->power.entry, &dpm_list);
新增device時,將entry加入到dpm_list,按照device的新增順序。
suspend()->pm_suspend()->enter_state()->suspend_devices_and_enter()->dpm_suspend_start()->
dpm_prepare()->list_move_tail(&dev->power.entry, &dpm_prepared_list)
suspend prepare階段,將dpm_list轉移到dpm_prepared_list,順序不變。
dpm_suspend()->list_move(&dev->power.entry, &dpm_suspended_list)
suspend階段,將dpm_prepared_list轉移到dpm_suspended_list,順序反轉。
dpm_resume_end()->dpm_resume()->list_move_tail(&dev->power.entry, &dpm_prepared_list)
resum階段,將dpm_suspended_list轉移到dpm_prepared_list,順序不變。
dpm_resume_end()->dpm_complete()->list_move(&dev->power.entry, &list);
list_splice(&list, &dpm_list);
resum complete階段,將dpm_prepared_list再轉移回到dpm_list,順序再次反轉。

3.8 enter_state()->suspend_finish()

static void suspend_finish(void)
{
	suspend_thaw_processes();
	pm_notifier_call_chain(PM_POST_SUSPEND);
	pm_restore_console();
}
在suspend_finish()函式中,解凍程序和任務,使能使用者空間helper程序,廣播一個系統從suspend狀態退出的notify,喚醒終端。
當所有的喚醒已經結束以後,使用者程序都已經開始運行了,但沒點亮螢幕,喚醒通常會是以下的幾種原因:
如果是來電,那麼Modem會通過傳送命令給rild來讓rild通知WindowManager有來電響應,這樣就會遠端呼叫PowerManagerService來寫”on”到 /sys/power/state 來呼叫late resume(),執行點亮螢幕等操作。
使用者按鍵事件會送到WindowManager中,WindowManager會處理這些按鍵事件,按鍵分為幾種情況,如果按鍵不是喚醒鍵,那麼WindowManager會主動放棄wakeLock來使系統進入再次休眠;如果按鍵是喚醒鍵,那麼WindowManger就會呼叫PowerManagerService中的介面來執行late resume。

3.9 late_resume()