前言:

 本着好奇的心态,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/Makefilepower_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 evnetobj-$(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结构体
......
//batterybattery_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;//usbusb_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_THERMALstruct thermal_zone_device *tzd;struct thermal_cooling_device *tcd;
#endif#ifdef CONFIG_LEDS_TRIGGERSstruct 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);                                  //记数加1queue_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告诉应用层。

[Android/Linux]-1.power_supply框架初识相关推荐

  1. Android/Linux Thermal框架分析及其Governor对比

    https://www.cnblogs.com/arnoldlu/p/6388151.html 图表 1 Thermal框架 随着SoC性能的快速提升,功耗也极大提高,带来的负面影响是SoC的温度提高 ...

  2. Android(Linux)实时监控串口数据

    之前在做WinCE车载方案时,曾做过一个小工具TraceMonitor,用于显示WinCE系统上应用程序的调试信息,特别是在实车调试时,用于监控和显示CAN盒与主机之间的串口数据.因为需要抢占市场先机 ...

  3. Android/linux(earlysuspend、lateresume)睡眠唤醒机制简

    来源处 http://blog.sina.com.cn/s/blog_759dc36b0100stax.html 背景介绍: 睡眠/唤醒是嵌入式Linux非常重要的组成部分,因为优秀的睡眠唤醒机制可以 ...

  4. 浅入浅出 Android 安全:第二章 Android Linux 内核层安全

    第二章 Android Linux 内核层安全 来源:Yury Zhauniarovich | Publications 译者:飞龙 协议:CC BY-NC-SA 4.0 作为最广为人知的开源项目之一 ...

  5. Android 2018优秀开源框架整理收藏

    中级.高级.资深工程师 知其然知其不可然 <框架百大排行榜>里所提到的流行词.流行术语--使用能力.融会贯通其原理.讲解框架能力的高低,将让你不断的在这三个级别徘徊: 会有意识的合并榜单里 ...

  6. linux使用flask设计网站,linux下Flask框架搭建简单网页

    开始安装FLASK需要创建一个虚拟环境,虚拟环境可以不干扰正在使用的系统环境,避免影响,并且也不需要完全的root权限,更加安全可靠. 搭建环境 Python3.4 进入到microblog目录下创建 ...

  7. android 优秀的开源框架整理

    程序员界有个神奇的网站,那就是github,这个网站集合了一大批优秀的开源框架,极大地节省了开发者开发的时间,在这里我进行了一下整理,这样可以使我们在使用到时快速的查找到,希望对大家有所帮助! 1. ...

  8. Android/Linux 子系统Graphics图形栈入门普法介绍

        Android/Linux 子系统Graphics图形栈入门普法介绍 写在最前面   由于工作原因,最近在公司做了一个关于Android/Linux 子系统Graphics图形栈入门相关知识的 ...

  9. Android应用开发五大框架,android 五大应用开发框架是什么

    android应用开发框架是 Application Framework. 其系统架构由5部分组成,分别是:Linux Kernel.Android Runtime.Libraries.Applica ...

最新文章

  1. 从零开始学习python-从零开始学习python(一)
  2. 5种Python统计次数的方法
  3. 程序员,你喜欢抽哪种香烟?(python数据分析)
  4. LIS路径记录(UVA481)
  5. iBATIS.NET 学习笔记(八)
  6. 解决win2003安装exchangeServer后关机慢的方法
  7. 什么是大数据,怎么理解和应对大数据时代
  8. github 代理_GitHub访问提速方法
  9. python异步网络通信框架_超级快的 Python 异步网络框架
  10. 斐波那契数列【java实现】
  11. android自定义手势解锁View
  12. 深入理解 Android 消息机制原理
  13. matlab高斯求积法_实验3:利用SVM实现线性高斯分类
  14. radius认证服务器系统,03-Radius认证配置举例
  15. 微软SQL Server BI认证专家QQ群36882826
  16. 阿里巴巴与星巴克合作 AR场景识别首次大规模商用
  17. fscanf php,php fscanf 函数_PHP教程
  18. javafx控件Button
  19. 上网看视频国家版八段锦,很好的预防和治疗久坐办公室带来的肩周颈椎疾病...
  20. part-21 总谐波失真THD

热门文章

  1. 无线802.1x认证服务器,TP-Link无线路由器+Radius认证服务器实现无线终端802.1X认证...
  2. 数据分析——Kettle插件开发异常信息总结
  3. 办公技巧整合(不定时更新)
  4. web页面内调取QQ应用
  5. [激光原理与应用-21]:《激光原理与技术》-7- 激光技术大汇总与总体概述
  6. 解决 Maven工程运行报错Failed to clean project: Failed to delete
  7. HBase原理 | HBase分区影响与合理分区设置
  8. SAP那些事-职业篇-35-PA考试通过啦
  9. Paypal账户注册教程!
  10. hexo 博客创建、部署、美化过程记录