修改驱动程序支持电源管理

在上上篇文章中,我们梳理过流程,休眠前后和驱动打交道主要就这两个函数:

a. 通知notifier:
在冻结APP之前,使用pm_notifier_call_chain(PM_SUSPEND_PREPARE)来通知驱动程序
在重启APP之后,使用pm_notifier_call_chain(PM_POST_SUSPEND)来通知驱动程序

如果驱动程序有事情在上述时机要处理,可以使用register_pm_notifier注册一个notifier。

int pm_notifier_call_chain(unsigned long val)
{
	int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL);

	return notifier_to_errno(ret);
}

int register_pm_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&pm_chain_head, nb);
}

先看看结构体
struct notifier_block {
notifier_fn_t notifier_call;
struct notifier_block __rcu *next;
int priority;
};
这里面有一个nofifi
示例代码:

//定义一个notifier_block结构体,并给其notifier_call函数赋值
static struct notifier_block lcd_pm_notif_block = {
	.notifier_call = lcd_suspend_notifier,
};

static int lcd_suspend_notifier(struct notifier_block *nb,
				unsigned long event,
				void *dummy)
{

	switch (event) {
	//休眠时会走到这里
	case PM_SUSPEND_PREPARE: 
		printk("lcd suspend notifiler test: PM_SUSPEND_PREPARE\n");
		return NOTIFY_OK;
	//唤醒后会走这里
	case PM_POST_SUSPEND:
		printk("lcd suspend notifiler test: PM_POST_SUSPEND\n");
		return NOTIFY_OK;

	default:
		return NOTIFY_DONE;
	}
}

驱动程序的冻结方法应该是在冻结应用进程之后,因为app有可能还在使用驱动,方法1pm_notifier_call_chain(PM_SUSPEND_PREPARE)是在冻结应用之前调用的。所以一般是使用方法2,如下:
方法2:通过平台框架来写suspend和resume函数

通过前面介绍,让设备进入休眠会走入一下流程

对于该设备,调用它的dev->pm_domain->ops->suspend	或
								                   dev->type->pm->suspend       或
								                   dev->class->pm->suspend      或
								                   dev->bus->pm->suspend        或
								                   dev->driver->pm->suspend
dev即
struct platform_driver {
	int (*probe)(struct platform_device *);
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;

	const struct dev_pm_ops *pm; //pm结构体

	struct driver_private *p;
};

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);
	...
};

static struct platform_device lcd_dev = {
    .name         = "mylcd",
    .id       = -1,
    .dev = { 
    	.release = lcd_release, 
	},
};

struct platform_driver lcd_drv = {
	.probe		= lcd_probe,
	.remove		= lcd_remove,
	.driver		= {
		.name	= "mylcd",
		.pm     = &lcd_pm,
	}
};
//定义结构体的suspend和resume
static struct dev_pm_ops lcd_pm = {
	.suspend = lcd_suspend,
	.resume  = lcd_resume,	
};

static int lcd_init(void)
{
	/* 电源管理 */
	register_pm_notifier(&lcd_pm_notif_block);

	platform_device_register(&lcd_dev);//注册平台设备
	platform_driver_register(&lcd_drv);//注册平台driver
	...
}

static int lcd_suspend(struct device *dev)
{
	lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD本身 */
	*gpbdat &= ~1;     /* 关闭背光 */
	return 0;
}

static int lcd_resume(struct device *dev)
{
	lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器 */
	lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本身 */
	*gpbdat |= 1;     /* 输出高电平, 使能背光 */		
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值