前言:

	本着好奇的心态,power_supply是如何将battery,ac,usb等的相关信息参数送到framework层以及应用层的,
所以了解了一下power_supply的大概。

引用:

在别的博客说得比较好的一问一答,借用一下:

问:安卓是怎么知道当前充电状态的,以及电池电量变化的?
答:是由底层(驱动层)主动通过uevent机制(实质是net_link方式的socket)(广泛应用于hotplug),充电插入与断开时,内核通过发送uevent信息,告诉android。
问:android如何知道各种参数并更新的?
答:通过kobject_uevent发送通知给上层,上层读取sys相关文件属性

安卓power_supply的大体框架图:
(后面补充)

power_supply驱动层代码分析:
1.power_supply的驱动层核心代码有以下几个个:

dir://kernel/drivers/power/Makefile

power_supply-y				:= power_supply_core.o
//power_supply的核心代码,提供power_supply的注册接口

power_supply-$(CONFIG_SYSFS)		+= power_supply_sysfs.o
//power_supply的文件系统,也就是需要用户层,会通过这些文件节点来获取信息

power_supply-$(CONFIG_LEDS_TRIGGERS)	+= power_supply_leds.o
//power_supply的led trigger ,在不同的状态触发不同的trgger evnet

obj-$(CONFIG_POWER_SUPPLY)	+= power_supply.o

在这里插入图片描述
2.注册一个power_supply实例,大概需要做几件事情呢?

参考:展讯平台的sprd_battery.c
对power_supply_decs 和power_supply_cfg两个结构体进行填充,然后注册就完事了
......
	struct power_supply *ret_ptr = NULL;
	struct power_supply_desc *battery_desc = NULL,
		*ac_desc = NULL, *usb_desc = NULL;
	struct power_supply_config battery_cfg = {}, ac_cfg = {}, usb_cfg = {};
.....

//填充 power_supply_desc结构体
......
//battery
	battery_desc = devm_kzalloc(&pdev->dev,
		sizeof(struct power_supply_desc), GFP_KERNEL);
	if (battery_desc == NULL) {
		ret = -ENOMEM;
		goto err_desc_alloc_failed;
	}
	battery_desc->properties = sprdbat_battery_props;
	battery_desc->num_properties = ARRAY_SIZE(sprdbat_battery_props);
	battery_desc->get_property = sprdbat_battery_get_property;
	battery_desc->set_property = sprdbat_battery_set_property;
	battery_desc->property_is_writeable =
			sprdbat_battery_property_is_writeable;
	battery_desc->name = "battery";
	battery_desc->type = POWER_SUPPLY_TYPE_BATTERY;
	battery_desc->no_thermal = true;
	battery_cfg.drv_data = sprdbat_data;
//ac
ac_desc = devm_kzalloc(&pdev->dev,
		sizeof(struct power_supply_desc), GFP_KERNEL);
	if (ac_desc == NULL) {
		ret = -ENOMEM;
		goto err_desc_alloc_failed;
	}
	ac_desc->properties = sprdbat_ac_props;
	ac_desc->num_properties = ARRAY_SIZE(sprdbat_ac_props);
	ac_desc->get_property = sprdbat_ac_get_property;
	ac_desc->set_property = sprdbat_ac_set_property;
	ac_desc->property_is_writeable =
			sprdbat_ac_property_is_writeable;

	ac_desc->name = "ac";
	ac_desc->type = POWER_SUPPLY_TYPE_MAINS;
	ac_desc->no_thermal = true;
	ac_cfg.drv_data = sprdbat_data;

//usb
	usb_desc = devm_kzalloc(&pdev->dev,
		sizeof(struct power_supply_desc), GFP_KERNEL);
	if (usb_desc == NULL) {
		ret = -ENOMEM;
		goto err_desc_alloc_failed;
	}
	usb_desc->properties = sprdbat_usb_props;
	usb_desc->num_properties = ARRAY_SIZE(sprdbat_usb_props);
	usb_desc->get_property = sprdbat_usb_get_property;
	usb_desc->name = "usb";
	usb_desc->type = POWER_SUPPLY_TYPE_USB;
	usb_desc->no_thermal = true;
	usb_cfg.drv_data = sprdbat_data;

	ret_ptr = power_supply_register(&pdev->dev, battery_desc, &battery_cfg);
	if (IS_ERR(ret_ptr)) {
		goto err_battery_failed;
	} else {
		data->battery = ret_ptr;
		data->battery->supplied_to = battery_supply_list;
		data->battery->num_supplicants =
			ARRAY_SIZE(battery_supply_list);
	}

	ret_ptr = power_supply_register(&pdev->dev, ac_desc, &ac_cfg);
	if (IS_ERR(ret_ptr)) {
		goto err_ac_failed;
	} else {
		data->ac = ret_ptr;
		data->ac->supplied_to = supply_list;
		data->ac->num_supplicants = ARRAY_SIZE(supply_list);
	}

	ret_ptr = power_supply_register(&pdev->dev, usb_desc, &usb_cfg);
	if (IS_ERR(ret_ptr)) {
		goto err_usb_failed;
	} else {
		data->usb = ret_ptr;
		data->usb->supplied_to = supply_list;
		data->usb->num_supplicants = ARRAY_SIZE(supply_list);
	}

//注册
	ret_ptr = power_supply_register(&pdev->dev, battery_desc, &battery_cfg);
	if (IS_ERR(ret_ptr)) {
		goto err_battery_failed;
	} else {
		data->battery = ret_ptr;
		data->battery->supplied_to = battery_supply_list;
		data->battery->num_supplicants =
			ARRAY_SIZE(battery_supply_list);
	}

	ret_ptr = power_supply_register(&pdev->dev, ac_desc, &ac_cfg);
	if (IS_ERR(ret_ptr)) {
		goto err_ac_failed;
	} else {
		data->ac = ret_ptr;
		data->ac->supplied_to = supply_list;
		data->ac->num_supplicants = ARRAY_SIZE(supply_list);
	}

	ret_ptr = power_supply_register(&pdev->dev, usb_desc, &usb_cfg);
	if (IS_ERR(ret_ptr)) {
		goto err_usb_failed;
	} else {
		data->usb = ret_ptr;
		data->usb->supplied_to = supply_list;
		data->usb->num_supplicants = ARRAY_SIZE(supply_list);
	}

3.接下来我们看看power_supply_decs和power_supply_config这两个结构体:
power_supply_decs结构体:

dir://kernel/include/linux/power_supply.h

/* Description of power supply */
struct power_supply_desc {
	const char *name; 														//名字
	enum power_supply_type type;									        //类型,usb,battery这种		
	enum power_supply_property *properties;                                 //属性,就是这个power_supply设备具备的属性,类似battery 的POWER_SUPPLY_PROP_CAPACITY(电量)属性
	size_t num_properties;													//属性的总数

	/*
	 * Functions for drivers implementing power supply class.
	 * These shouldn't be called directly by other drivers for accessing
	 * this power supply. Instead use power_supply_*() functions (for
	 * example power_supply_get_property()).
	 */
	int (*get_property)(struct power_supply *psy,
			    enum power_supply_property psp,
			    union power_supply_propval *val);    						 //在c编程中,我们叫函数指针,我们也可以抽象的认为是类编程中的抽象方法,就是要实现一个获取属性的函数
	int (*set_property)(struct power_supply *psy,
			    enum power_supply_property psp,
			    const union power_supply_propval *val);						//同上,获取属性
	/*
	 * property_is_writeable() will be called during registration
	 * of power supply. If this happens during device probe then it must
	 * not access internal data of device (because probe did not end).
	 */
	int (*property_is_writeable)(struct power_supply *psy,
				     enum power_supply_property psp);						//同上
	void (*external_power_changed)(struct power_supply *psy);
	void (*set_charged)(struct power_supply *psy);

	/*
	 * Set if thermal zone should not be created for this power supply.
	 * For example for virtual supplies forwarding calls to actual
	 * sensors or other supplies.
	 */
	bool no_thermal;														//这个power_supply设备是否支持thermal(温度相关,防止高温烧坏器件等等)
	/* For APM emulation, think legacy userspace. */
	int use_for_apm;
};

// power_supply_config结构体,好像没有什么好介绍的

/* Run-time specific power supply configuration */
struct power_supply_config {
	struct device_node *of_node; 
	/* Driver private data */
	void *drv_data;

	char **supplied_to;
	size_t num_supplicants;
};

power_supply 结构体

struct power_supply {
	const struct power_supply_desc *desc;

	char **supplied_to;
	size_t num_supplicants;

	char **supplied_from;
	size_t num_supplies;
	struct device_node *of_node;

	/* Driver private data */
	void *drv_data;

	/* private */
	struct device dev;
	struct work_struct changed_work;	//为每个power_supply设备创建一个工作队列,在调用power_supply_changed的时候调用
	struct delayed_work deferred_register_work; //为每个power_supply设备创建一个延时工作队列,在第一次调用power_supply_register的时候会调用,用来通知应用层,以便实现数据更新
	spinlock_t changed_lock;
	bool changed;
	atomic_t use_cnt;
#ifdef CONFIG_THERMAL
	struct thermal_zone_device *tzd;
	struct thermal_cooling_device *tcd;
#endif

#ifdef CONFIG_LEDS_TRIGGERS
	struct led_trigger *charging_full_trig;
	char *charging_full_trig_name;
	struct led_trigger *charging_trig;
	char *charging_trig_name;
	struct led_trigger *full_trig;
	char *full_trig_name;
	struct led_trigger *online_trig;
	char *online_trig_name;
	struct led_trigger *charging_blink_full_solid_trig;
	char *charging_blink_full_solid_trig_name;
#endif
};

3.power_supply_regitser源码分析:

static struct power_supply *__must_check
__power_supply_register(struct device *parent,
				   const struct power_supply_desc *desc,
				   const struct power_supply_config *cfg,
				   bool ws)
{
	struct device *dev;
	struct power_supply *psy;
	int rc;

	if (!parent)
		pr_warn("%s: Expected proper parent device for '%s'\n",
			__func__, desc->name);

	psy = kzalloc(sizeof(*psy), GFP_KERNEL);
	if (!psy)
		return ERR_PTR(-ENOMEM);

	dev = &psy->dev;

	device_initialize(dev); 								//初始化设备结构体

	dev->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->desc = desc;										//power supply desc 
	if (cfg) {												//power supply config 结构体
		psy->drv_data = cfg->drv_data;
		psy->of_node = cfg->of_node;
		psy->supplied_to = cfg->supplied_to;
		psy->num_supplicants = cfg->num_supplicants;
	}

	rc = dev_set_name(dev, "%s", desc->name);				//命名设备名
	if (rc)
		goto dev_set_name_failed;

	INIT_WORK(&psy->changed_work, power_supply_changed_work);//初始化power_supply_changed工作队列
	INIT_DELAYED_WORK(&psy->deferred_register_work,			//初始化power_supply_regiser延时工作队列
			  power_supply_deferred_register_work);

	rc = power_supply_check_supplies(psy);						//在链表中查询实例power_supply是否存在
	if (rc) {
		dev_info(dev, "Not all required supplies found, defer probe\n");
		goto check_supplies_failed;
	}

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

	rc = device_add(dev);										//将设备添加到设备层
	if (rc)
		goto device_add_failed;

	rc = psy_register_thermal(psy);								//注册thermal ,如果no_thermal,则相当于无操作
	if (rc)
		goto register_thermal_failed;

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

	rc = power_supply_create_triggers(psy);						//注册led的触发器
	if (rc)
		goto create_triggers_failed;

	/*
	 * Update use_cnt after any uevents (most notably from device_add()).
	 * We are here still during driver's probe but
	 * the power_supply_uevent() calls back driver's get_property
	 * method so:
	 * 1. Driver did not assigned the returned struct power_supply,
	 * 2. Driver could not finish initialization (anything in its probe
	 *    after calling power_supply_register()).
	 */
	atomic_inc(&psy->use_cnt);									//记数加1

	queue_delayed_work(system_power_efficient_wq,				//每个电源应用注册后,延时调用power_supply_changed来更新下数据
			   &psy->deferred_register_work,
			   POWER_SUPPLY_DEFERRED_REGISTER_TIME);

	return psy;

create_triggers_failed:
	psy_unregister_cooler(psy);
register_cooler_failed:
	psy_unregister_thermal(psy);
register_thermal_failed:
	device_del(dev);
device_add_failed:
wakeup_init_failed:
check_supplies_failed:
dev_set_name_failed:
	put_device(dev);
	return ERR_PTR(rc);
}

/**
 * power_supply_register() - Register new power supply
 * @parent:	Device to be a parent of power supply's device, usually
 *		the device which probe function calls this
 * @desc:	Description of power supply, must be valid through whole
 *		lifetime of this power supply
 * @cfg:	Run-time specific configuration accessed during registering,
 *		may be NULL
 *
 * Return: A pointer to newly allocated power_supply on success
 * or ERR_PTR otherwise.
 * Use power_supply_unregister() on returned power_supply pointer to release
 * resources.
 */
struct power_supply *__must_check power_supply_register(struct device *parent,
		const struct power_supply_desc *desc,
		const struct power_supply_config *cfg)
{
	return __power_supply_register(parent, desc, cfg, true);
}
EXPORT_SYMBOL_GPL(power_supply_register);

4.power_supply_changed源码分析:

static int __power_supply_changed_work(struct device *dev, void *data)
{
	struct power_supply *psy = data;
	struct power_supply *pst = dev_get_drvdata(dev);

	if (__power_supply_is_supplied_by(psy, pst)) {
		if (pst->desc->external_power_changed)
			pst->desc->external_power_changed(pst);
	}

	return 0;
}

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

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

	spin_lock_irqsave(&psy->changed_lock, flags);
	/*
	 * Check 'changed' here to avoid issues due to race between
	 * power_supply_changed() and this routine. In worst case
	 * power_supply_changed() can be called again just before we take above
	 * lock. During the first call of this routine we will mark 'changed' as
	 * false and it will stay false for the next call as well.
	 */
	if (likely(psy->changed)) {
		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);								//
		atomic_notifier_call_chain(&power_supply_notifier,			//事件通知链,主要用于内核通知
				PSY_EVENT_PROP_CHANGED, psy);
		kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE);				// 通过uevnet来通知应用层,有数据需要更新。
		spin_lock_irqsave(&psy->changed_lock, flags);
	}

	/*
	 * Hold the wakeup_source until all events are processed.
	 * power_supply_changed() might have called again and have set 'changed'
	 * to true.
	 */
	if (likely(!psy->changed))
		pm_relax(&psy->dev);
	spin_unlock_irqrestore(&psy->changed_lock, flags);
}

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); //调度工作队列
}
EXPORT_SYMBOL_GPL(power_supply_changed);

总结:

总的来说:在驱动层浅显的理解power_supply框架就是:
	所有power_supply设备通过实现power_supply_decs结构体的填充实现,来实现对power_supply设备
	具备的属性描述,诸如:battery具备capacity, ac具备online这种,然后再实现对其拥有属性的设置
	和读取的函数,最后调用power_supply_register()将该power_supply设备注册上去。最后通过
	power_supply_changed()来将power_supply设备有数据变化通过uevent告诉应用层。
Logo

更多推荐