power supply是如何上报电池信息的

一、引文

作为一个内核初学者,经常容易进入“知其然但不知其所以然”的状态,在power supply子系统中就是这样,知道如何去添加一个属性prop,知道psy可以创建一堆文件节点,也知道上层是通过读取这些节点来获取供电信息的,但对于其中的细节,便知之甚少。最近深究其中,才逐步发现内核的奥妙所在。

二、Android供电系统框架

power supply是如何上报电池信息的

power supply(以下简称psy)是Linux中从供电驱动抽象出来的子系统,是Linux电源管理的重要组成部分。psy是一个中间层,在kernel中是属于设备驱动的一部分,psy的作用主要是向用户空间汇总各类供电的状态信息。抽象出来的各类信息称为property,比如供电设备是否连接就对应着POWER_SUPPLY_PROP_ONLINE。在驱动层,主要是两大模块,与电池相关的驱动和与充放电管理相关的驱动(对应图中的battery.c和charger.c),这两大模块主要处理硬件相关的逻辑,在硬件状态发生变化时,会触发相关的中断,驱动层会调用相应的中断函数,并更新修改相应的psy节点值。驱动负责更新psy节点的状态,HAL层会去读这些节点,驱动在检测硬件、传感器信息变化会去更新节点值,而HAL层什么时候会去读取这些节点值呢?以及其中调用的流程是怎样的?今天就简单介绍下。

三、power supply子系统简介

1. 概述

psy子系统的基础是建立在设备驱动模型之上的,主要运用了其中的class、device、kobject、sysfs、uevent相关知识,也是驱动设备模型的一个具体应用。psy子系统中power_supply_class对应着系统中供电设备类,是一个抽象化的集合,对应着/sys/class/power_supply/目录,供电设备都在该目录之下,比如battery设备就对应该目录下一个子目录battery,而battery设备的一个属性则对应battery的一个文件节点,也对应着一个kobject。

2. 相关结构体

psy相关的定义在
/include/linux/power_supply.h。

power supply是如何上报电池信息的

psy_desc是psy子系统中最重要的结构体,该描述符定义了psy的属性property,以及相关的set/get/is_writeable接口,is_writeable即文件节点的“w”权限,所有节点默认是可读的。从get函数可以看到调用该函数需要指定某个psy和psp(属性),结果保存在val中,值得一提的是,val是个union类型,可以传递int或char *。

power supply是如何上报电池信息的

struct power_supply表示一个psy供电设备,比如电池、AC、USB,一般可通过
devm_power_supply_register函数注册成一个psy设备,在注册设备之前需要定义好该设备的psy_desc。

3. 相关接口函数

相关的函数主要在psy_core.c和psy_sysfs.c中,core主要负责设备状态变化逻辑,sysfs主要负责文件节点相关逻辑。

最重要的是power_supply_changed,在驱动中检测到硬件状态发生变化,会通过该函数调度起psy中的changed_work。该工作队列负责发送notifier(内核内不同模块之间)和通过uevent进行change上报。

power supply是如何上报电池信息的

__power_supply_register负责注册一个psy设备:

power supply是如何上报电池信息的

在sysfs中有个power_supply_uevent,该函数在psy class初始化时注册为设备节点的dev_uevent,在每一个psy目录下都有一个uevent节点,读取该节点即调用power_supply_uevent函数。该函数遍历当前设备下的所有属性并将结果保存在kobj_uevent_env中,结果以键值对的形式进行保存。

power supply是如何上报电池信息的

  1. 调用流程

psy子系统主要调度的逻辑都在power_supply_changed_work中。跟踪这一调用流程可以在驱动中实现的get_property函数增加调用栈打印:

power supply是如何上报电池信息的

可以看到,在kobject_uevent_env函数中调用对应kset的uevent,会去遍历每一个属性节点(dev_uevent),之后,会通过netlink_broadcast函数进行广播(使用netlink机制),其中广播的字符串保存在sk_buff->data中,这一字符串以“action_string@devpath”进行拼接,其中action为kobject_action,而devpath则为该psy设备的设备路径。值得注意的是,使用uevent-netlink机制传递的字符串并不会包含psy属性节点的kobject_uevent_env键值对状态。

四、healthd简介

由于uevent机制仅将一个简单的字符串传递给了用户空间,而安卓系统建立在kernel之上,需要思考如何将设备属性的变化值及时更新到用户空间,于是就有了healthd服务,healthd目前已经更新到了2.1版本,其主要工作通过epoll_wait来监听kernel中的uevent事件。具体的函数调用流程图如下:

power supply是如何上报电池信息的

相关的代码路径主要是在:

/hardware/interfaces/health/;

/system/core/healthd/;

从service.cpp开始理一下调用流程,可以整理出上述的调用流程,黑色线条为初始化流程,红色线条为当psy-uevent上报后触发epoll之后的调用流程。与底层节点交互的逻辑都在BatteryMonitor中,在初始化过程中会初始化healthd_config结构体,用于保存psy属性节点的路径。

在监听循环MainLoop函数中,一个while(1)循环,调用epoll_wait()函数来监听uevent,收到事件之后会调用初始化时注册好的func(UeventEvent),该函数会通过
uevent_kernel_multicast_recv接口去读取netlink发送的sk_buff->data,通过查找其中的字符串来判断事件是否为psy子系统发送的,如果不是的话,不会进行处理。进一步的处理流程主要是调用到BatteryMonitor中的updateValues,在该函数中会遍历读取psy属性节点,存储在HealthInfo结构体中,之后通过BinderHealth中注册好的回调函数IHealthInfoCallback通知BatterySerice,具体的通知函数为BinderHealth:OnHealthInfoChanged。

Healthd是一个根植于powersupply子系统,并采用了epoll监听底层节点的uevent事件,之后轮询底层属性节点的守护进程。在安卓R版本中,Healthd相关代码重构为libhealthloop和libhealth2impl,但为了保证向后兼容,可以看到在ScheduleBatteryUpdate()函数中调用了两次updateValues,这样会遍历两次底层节点造成了冗余。另外在psy-uevent机制中,也有一次属性节点的遍历,一共三次遍历,这就要求底层驱动在更新属性值时,不能加入耗时的IO操作,否则会影响系统性能。

五、总结

power supply架构的精髓是极大化的发挥了uevent和sysfs的作用,简单高效地抽象出了与硬件无关的关键信息,通过notify机制使得其他内核模块可以及时获取相关事件;Healthd通过epoll监听psy创建的节点uevent,之后再去遍历读取结果,这样是为了避免与kernel的耦合。psy和Healthd比较适合新手学习,能提供一个由外向内的视角去解读kernel,也能加深对设备驱动模型的理解。熟悉之后可以进行相关的逻辑扩展,也可以进一步学习usb模块与psy子系统的关系,也可以进一步探究涉及的notify、netlink、epoll等机制。

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Power Supply驱动是用于管理和监控供电设备的驱动程序。它负责与硬件交互,提供供电设备的状态信息,并处理供电设备的属性变化。 在Linux系统中,Power Supply驱动的头文件为`include/linux/power_supply.h`,驱动框架的代码位于`drivers/power/power_supply_core.c`和`drivers/power/power_supply_sysfs.c`。\[1\] Power Supply驱动的节点是通过调用`power_supply_register`函数注册的。在初始化过程中,会在`/sys/class`目录下创建`power_supply`目录,并注册uevent回调函数,初始化节点。`power_supply_desc`结构体中的`properties`字段指定了相应的属性类型,根据这些属性类型会注册相应的节点。\[2\] 一个Power Supply驱动需要实现`get_property`和`external_power_changed`这两个函数,这些函数的名称对应于在`/sys/class/power_supply/`目录中创建的子目录。`power_supply_property`是一系列用枚举值表示的属性,对应于供电设备子目录中的文件名。`supplied_to`表示为哪个设备进行供电,通常指某个电池。\[3\] 总结起来,Power Supply驱动是用于管理和监控供电设备的驱动程序,它通过注册节点和属性来提供供电设备的状态信息,并处理供电设备的属性变化。 #### 引用[.reference_title] - *1* *3* [Power Supply驱动框架](https://blog.csdn.net/gnnulzy/article/details/51462762)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Linux power_supply子系统](https://blog.csdn.net/qq_39678541/article/details/122964116)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一口Linux

众筹植发

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值