Linux Power supply子系统分析之二

1.概述

在上一篇博文中参考窝窝科技的文章分析了linux Power Supply子系统的框架。这篇我们以一个实际的例子来看一下PSY driver的编程方式,比便于更深刻理解power supply 子系统。

2.power supply子系统的引入

以市面上一款常见的的平板方案来看一看,进入sys/class/power_supply/目录下


可以看到这里有三个PSY设备,分别对应USB充电器 DC充电器,和电池。

进入battery目录下,发现下面有各种各样的属性,另外两个atc260x-usb 、atc260x-wall目录下分别也是这样。


那么在内核中肯定有三个驱动对应这个三个PSY设备。

3.PSY设备驱动分析

我们先以battery驱动为例来分析。

static int __init atc260x_gauge_init(void)
{
	struct device_node *node =
		of_find_compatible_node(NULL, NULL, "actions,atc2603c-battery");//获取设备树中对应的属性节点
	if (!node) {
		GAUGE_INFO("%s fail to find atc2603c-battery node\n", __func__);
		return 0;
	}
	GAUGE_INFO("atc2603c_battery:version(%s), time stamp(%s)\n",
		ATC2603C_BATTERY_DRV_VERSION, ATC2603C_BATTERY_DRV_TIMESTAMP);
	return platform_driver_register(&atc260x_gauge_driver);   //将PSY设备驱动注册为platform设备驱动
}

看看atc260x_gauge_driver的probe函数。

static  int atc260x_gauge_probe(struct platform_device *pdev)
{
	struct atc260x_gauge_info *info; //
	int ret;

	info = kzalloc(sizeof(struct atc260x_gauge_info), GFP_KERNEL); //分配一个atc260x_gauge_info
	if (info == NULL)
		return -ENOMEM;

	info->atc260x = atc260x;
	info->node = pdev->dev.of_node;   //一些初始化工作
	global_gauge_info_ptr = info;
	first_store_gauge_info = 1;

	mutex_init(&info->lock);

	platform_set_drvdata(pdev, info);

	/*init battery power supply*/          //这里就是power supply相关的了
	info->battery.name = "battery";        //这name “battery”就是对应我们在/sys/class/power_supply/目录下看到的battery
	info->battery.use_for_apm = 1;
	info->battery.type = POWER_SUPPLY_TYPE_BATTERY; //设备type
	info->battery.properties = atc260x_gauge_props; //设置属性列表
	info->battery.num_properties = ARRAY_SIZE(atc260x_gauge_props); //设置属性列表的大小
	info->battery.get_property = atc260x_gauge_get_props;  //设置获取属性值的回调函数。
	ret = power_supply_register(&pdev->dev, &info->battery); //将这个power_supply设备注册进内核中。
	
        。。。//其他电量计相关的省略

	return ret;
}

从上面可以看出 编写PSY driver相当的简单。就调用了一个power_supply_register函数。

后面再电量计的轮询函数中,如果检测到相关的属性发生了变化。就调用power_supply_changed函数,上报给power supply core。

static void soc_post_process(struct atc260x_gauge_info *info)
{
	int soc_last;

	if (info->soc_pre != info->soc_show) {
		info->soc_pre = info->soc_show;
		power_supply_changed(&info->battery); //如果soc发生了变化,则调用power_supply_changed上报变化信息。
	}

	info->bat_temp = get_battery_temperature();
	if (info->pre_temp!= info->bat_temp) {
		info->pre_temp = info->bat_temp;
		power_supply_changed(&info->battery); //如果温度发生了变化也进行上报
	}
        //省略其他不相关的。。。
}

对于USB 和WALL 的PSY设备,其编程方法也是类似的,这里不再赘述。

4.power supply framework的分析

先从power_supply_register分析起

int power_supply_register(struct device *parent, struct power_supply *psy)
{
	struct device *dev;
	int rc;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配一个device 结构
	if (!dev)
		return -ENOMEM;

	device_initialize(dev);

	dev->class = power_supply_class;  //指定power_supply_class
	dev->type = &power_supply_dev_type; //指定设备类型
	dev->parent = parent;               //指定此设备的父设备   
	dev->release = power_supply_dev_release;
	dev_set_drvdata(dev, psy);
	psy->dev = dev;

	INIT_WORK(&psy->changed_work, power_supply_changed_work); //初始化一个power_supply_changed_work 工做队列

	rc = power_supply_check_supplies(psy);
	if (rc) {
		dev_info(dev, "Not all required supplies found, defer probe\n");
		goto check_supplies_failed;
	}

	rc = kobject_set_name(&dev->kobj, "%s", psy->name);  //设置kobject的name,其实就是“battery”
	if (rc)
		goto kobject_set_name_failed;

	rc = device_add(dev);  //将这个设备添加到设备链表中去
	if (rc)
		goto device_add_failed;

	spin_lock_init(&psy->changed_lock);
	rc = device_init_wakeup(dev, true);
	if (rc)
		goto wakeup_init_failed;

	rc = psy_register_thermal(psy);
	if (rc)
		goto register_thermal_failed;

	rc = psy_register_cooler(psy);
	if (rc)
		goto register_cooler_failed;

	rc = power_supply_create_triggers(psy);
	if (rc)
		goto create_triggers_failed;

	power_supply_changed(psy); //调用一次changed,上报一次属性值

	goto success;
}

接着看看power_supply_changed

void power_supply_changed(struct power_supply *psy)
{
	unsigned long flags;

	dev_dbg(psy->dev, "%s\n", __func__);

	spin_lock_irqsave(&psy->changed_lock, flags);
	psy->changed = true;
	pm_stay_awake(psy->dev);
	spin_unlock_irqrestore(&psy->changed_lock, flags);
	schedule_work(&psy->changed_work); //将psy->changed_work任务提交到工做队列,这个工做队列就是在power_supply_register中初始化的
}
EXPORT_SYMBOL_GPL(power_supply_changed);

既然如此,那我们就应该继续去看psy->changed_work的工作队列函数。power_supply_changed_work

static void power_supply_changed_work(struct work_struct *work)
{
	unsigned long flags;
	struct power_supply *psy = container_of(work, struct power_supply, //获取到PSY
						changed_work);

	dev_dbg(psy->dev, "%s\n", __func__);

	spin_lock_irqsave(&psy->changed_lock, flags);
	if (psy->changed) {                    //已经在power_supply_changed中被设置成true了。
		psy->changed = false;
		spin_unlock_irqrestore(&psy->changed_lock, flags);

		class_for_each_device(power_supply_class, NULL, psy,
				      __power_supply_changed_work);

		power_supply_update_leds(psy);          //更改led指示状态

		kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); //发送uevent事件,通知应用层
		spin_lock_irqsave(&psy->changed_lock, flags);
	}
	if (!psy->changed)
		pm_relax(psy->dev);
	spin_unlock_irqrestore(&psy->changed_lock, flags);
}

至此,每当驱动中计算出battery的电量信息发生了变化,或者充电器的插拔状态发生了变化,驱动都会调用power_supply_changed函数,随后在power_supply_changed中会启动工作队列,上报uevent事件,通知Android层去sysfs中读取电池相关的属性信息,展现给用户空间。




  • 7
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值