[Android/Linux]-1.power_supply框架初识
前言:本着好奇的心态,power_supply是如何将battery,ac,usb等的相关信息参数送到framework层以及应用层的,所以了解了一下power_supply的大概。引用:在别的博客说得比较好的一问一答,借用一下:问:安卓是怎么知道当前充电状态的,以及电池电量变化的?答:是由底层(驱动层)主动通过uevent机制(实质是net_link方式的socket)(广泛应用于hotplug
·
前言:
本着好奇的心态,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告诉应用层。
更多推荐
已为社区贡献1条内容
所有评论(0)