1.概述
对于便携式设备,如手机或者pad来说,battery都是必不可少的一个组成部分。kernel中为了方便对battery的管理,专门提供了power supply framework。
battery管理我觉得可以分开为两个部分,一个是电池监控(fuelgauge),另一个是充放电管理(charger),所以我们在内核中也是把它分成了两个驱动来管理。
电池监控(fuelgauge)驱动主要是负责向上层android系统提供当前电池的电量以及健康状态信息等等,另外除了这个以外,它也向charger驱动提供电池的相关信息;
充放电管理(charger)驱动主要负责电源线的插拔检测,以及充放电的过程管理。对于battery管理,硬件上有电量计IC和充放电IC。
2.Power Supply设计思路
先来回答一个问题:kernel中设备驱动的目的,是管理设备,并提供给用户空间程序使用,那么对PSY设备而言,kernel要管理什么?用户空间程序要使用什么?
其实PSY设备是一个特例,它的目的很单纯,就为系统供电。如果只考虑这个目的,就不需要任何驱动了,但情况会稍微复杂,因为:
1.PSY设备可能是电池,这引申出来电量检测, 充电管理等多个议题。此时,PSY driver需要管理的事情包括:检测电池类型,检测电池电量;检测充电状态等等。而用户空间则需要将检测到的结果显示给用户。
2.系统中可能有多个PSY设备,这些设备可能还有级联关系,如有些平板电脑中,可能同时存在DC-charger、USB-charger和battery供电。此时,PSY driver需要管理的事情包括,获取外部供电设备的连接状态,充电状态等等,用户空间需要将这些信息显示给用户。
那么,共性已经总结出来了,PSY driver的主要功能,就是向用户空间汇报各类状态信息,因此power supply class的核心思路就是:将这些状态信息,抽象为属性“properties”。由于状态信息是类型是有限的,properties的个数也是有限的。
PSY driver只需要负责:该PSY设备具备有哪些“属性”,这些“属性”的值(value)是什么;当属性值发生改变时,通知power supply class。
power supply class负责:将某个PSY 设备支持的属性及value,以sysfs的形式,提供给用户空间,当属性值改变时,以uevent的形式,广播给用户空间程序。
另外,power supply class也会协助处理PSY级联的情况。
3.Power Supply软件架构
power supply framework在kernel/drivers/power/下。内核抽象出来power supply子系统为驱动提供了统一的框架。功能包括:
1.抽象PSY设备的共性,向用户空间提供统一的API
2.为底层PSY驱动的编写,提供简单、统一的方式。同事封装并实现公共逻辑。
power supply class位于drivers/power/目录中,主要由3部分组成(可参考下图的软件架构):
1)power supply core,用于抽象核心数据结构、实现公共逻辑。位于drivers/power/power_supply_core.c中。
2)power supply sysfs,实现sysfs以及uevent功能。位于drivers/power/power_supply_sysfs.c中。
3)power supply leds,基于linux led class,提供PSY设备状态指示的通用实现。位于drivers/power/power_suppply_leds.c中。
最后,驱动工程师可以基于power supply class,实现具体的PSY drivers,主要处理平台相关、硬件相关的逻辑。这些drivers都位于drivers/power/目录下。
接着来看一下核心数据结构drivers/power/power_supply_core.c中。
1.struct power_supplystruct power_supply为power supply class的核心数据结构,用于抽象PSY设备,定义如下(kernel/msm-4.19/include/linux/power_supply.h):
- struct power_supply {
- const char *name; //该PSY的名称
- enum power_supply_type type; //该PSY的类型,枚举类型,一般包括:battery、USB-charger、DC-charger。
- enum power_supply_property *properties; //该PSY具有的属性列表,枚举型。
- #ifdef CONFIG_CHARGING_NODEADD
Int charge_node_add //需要添加节点的话要在这里添加节点属性
#endif
- size_t num_properties; //属性的个数
- char **supplied_to; //一个字符串数组,保存了由该PSY供电的PSY列表,以此可将PAY组成互相级联的PSY链表。这些被供电的PSY,称作supplicant(客户端,乞求者)
- size_t num_supplicants;//supplicant的个数。
- char **supplied_from; //一个字符串数组,保存了该PSY供电的PSY列表,也称作supply(提供者),从另外一个方向组织PSY之间的级联关系。
- size_t num_supplies; //supply的个数
- #ifdef CONFIG_OF
- struct device_node *of_node; //用于保存of_node指针。
- #endif
- int (*get_property)(struct power_supply *psy, //PSY driver需要实现的回调函数,用于获取属性值。
- enum power_supply_property psp,
- union power_supply_propval *val);
- int (*set_property)(struct power_supply *psy, //PSY driver需要实现的回调函数,用于设置属性值。
- enum power_supply_property psp,
- const union power_supply_propval *val);
- int (*property_is_writeable)(struct power_supply *psy, //返回指定的属性值是否可写(用于sysfs);
- enum power_supply_property psp);
- void (*external_power_changed)(struct power_supply *psy); //当一个PSY设备存在supply PSY,且该supply PSY的属性发生改变(如online offline)时,power supply core会调用该回调函数,通知PSY driver以便让它做出相应处理。
- void (*set_charged)(struct power_supply *psy); //该回调函数的应用场景有点奇怪,外部模块通知PAY driver,该PSY设备的状态改变了,自己改变了自己不知道,要外部通知,希望大家在实际工作中不要遇到,太纠结了。
- int use_for_apm;
- struct device *dev;
- struct work_struct changed_work; //用户处理状态改变的work queue,主要思路是当该PSY的状态发生改变,启动一个workqueue,查询并通知所有的supplicants。
- spinlock_t changed_lock;
- bool changed;
- #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; //如果配置了CONFIC_LED_TRIGGERS,则调用的linux led class的接口,注册相应的LED设备,用于PSY状态指示。
- char *online_trig_name;
- struct led_trigger *charging_blink_full_solid_trig;
- char *charging_blink_full_solid_trig_name;
- #endif
- };
PSY类型由enum power_supply_type(power_supply.h)定义:
- enum power_supply_type {
- POWER_SUPPLY_TYPE_UNKNOWN = 0, //未知
- POWER_SUPPLY_TYPE_BATTERY, //电池,手机平板上面最常用的
- POWER_SUPPLY_TYPE_UPS, //不间断供电的,一般用户服务器
- POWER_SUPPLY_TYPE_MAINS, //主供电设备,如笔记本电脑适配器,特点是可以单独供电,当其断开时,再由辅助设备供电。
- POWER_SUPPLY_TYPE_USB,
- POWER_SUPPLY_TYPE_USB_DCP,
- POWER_SUPPLY_TYPE_USB_CDP,
- POWER_SUPPLY_TYPE_USB_ACA,
- };
power supply class将所有可能PSY属性(power_supply.h),以枚举型变量形式抽象出来,PSY driver可以根据设备的实际情况,从中选取一些。
- enum power_supply_property {
- POWER_SUPPLY_PROP_STATUS = 0, //该PSY的status,主要是充电状态,包括:unknown,charging,discharging,not charging full,
- POWER_SUPPLY_PROP_CHARGE_TYPE,//充电类型
- POWER_SUPPLY_PROP_HEALTH, //健康状况,包括:good dead over voltage等
- POWER_SUPPLY_PROP_PRESENT, //电量百分比
- POWER_SUPPLY_PROP_ONLINE, //是否在线
- POWER_SUPPLY_PROP_AUTHENTIC,
- POWER_SUPPLY_PROP_TECHNOLOGY, //采用的技术
- POWER_SUPPLY_PROP_CYCLE_COUNT,
- POWER_SUPPLY_PROP_VOLTAGE_MAX,
- POWER_SUPPLY_PROP_VOLTAGE_MIN,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_VOLTAGE_AVG,
- POWER_SUPPLY_PROP_VOLTAGE_OCV,
- POWER_SUPPLY_PROP_CURRENT_MAX,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_CURRENT_AVG,
- POWER_SUPPLY_PROP_POWER_NOW,
- POWER_SUPPLY_PROP_POWER_AVG,
- POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
- POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
- POWER_SUPPLY_PROP_CHARGE_FULL,
- POWER_SUPPLY_PROP_CHARGE_EMPTY,
- POWER_SUPPLY_PROP_CHARGE_NOW,
- POWER_SUPPLY_PROP_CHARGE_AVG,
- POWER_SUPPLY_PROP_CHARGE_COUNTER,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
- POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
- POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
- POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
- POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
- POWER_SUPPLY_PROP_ENERGY_FULL,
- POWER_SUPPLY_PROP_ENERGY_EMPTY,
- POWER_SUPPLY_PROP_ENERGY_NOW,
- POWER_SUPPLY_PROP_ENERGY_AVG,
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
- POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX,
- POWER_SUPPLY_PROP_CAPACITY_LEVEL, //容量
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
- POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
- POWER_SUPPLY_PROP_TEMP_AMBIENT,
- POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
- POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
- POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
- POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
- POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
- POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
- POWER_SUPPLY_PROP_TYPE,
- POWER_SUPPLY_PROP_SCOPE,
- POWER_SUPPLY_PROP_USB_HC,
- POWER_SUPPLY_PROP_USB_OTG,
- POWER_SUPPLY_PROP_CHARGE_ENABLED,
- POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
- POWER_SUPPLY_PROP_MODEL_NAME,
- POWER_SUPPLY_PROP_MANUFACTURER,
- POWER_SUPPLY_PROP_SERIAL_NUMBER,
- };
power supply class 首要任务,是向PSY driver提供统一的驱动编程接口,主要包括:
1.PSY 的register/unregister API
在driver/power/power_supply_core.c中
- extern int power_supply_register(struct device *parent,
- struct power_supply *psy);
- extern void power_supply_unregister(struct power_supply *psy);
- extern int power_supply_powers(struct power_supply *psy, struct device *dev);
2.PSY状态改变时通知power supply core的API
extern void power_supply_changed(struct power_supply *psy);
当PSY driver检测到该设备某些属性改变时,需要调用这个接口(power_supply_changed(struct power_supply *psy)),通知power supply core,power supply core会有如下动作
如果该PSY是其他PSY的供电源,调用这些PSY的external_power_changed回调函数,通知他们(这些PSY具体要做什么,由他们自身的逻辑决定);
发生notifier通知那些关心PSY设备状态的drivers;
以统一的格式向用户空间发送uevent。(这就是设备模型中class的魅力,对外接口由class core提供,可以节省driver的工作量,同时确保了接口的一致性)。
3.其他杂项接口
- extern struct power_supply *power_supply_get_by_name(const char *name); //通过名字获取PSY指针
- extern int power_supply_am_i_supplied(struct power_supply *psy);//查询自己是否由其他PSY供电
- extern int power_supply_set_battery_charged(struct power_supply *psy);//调用指定的PSY的set_changed回调。
- extern int power_supply_is_system_supplied(void); //检查系统中是否有有效的或者处于online状态的PSY,如果没有,可能为桌面系统、
- extern int power_supply_powers(struct power_supply *psy, struct device *dev);//在指定的设备的sysfs目录下创建指定的PSY符合连接。
5.向用户空间提供的API
power supply class通过两种形式向用户空间提供接口。
1)uevent,以“名字=value”的形式,上报所有property的值,格式如下:
- POWER_SUPPLY_NAME=xxx
- POWER_SUPPLY_xxx1=xxx
- POWER_SUPPLY_xxx2=xxx
uevent一般会在PSY设备添加到kernel时,或者PSY属性发生改变时发生。
2)sysfs,power supply class在power_supply_sysfs.c中,定义了相当多的默认attribute,如果某个PSY设备具有某个属性,该属性对应的attribute就会体现在sysfs中(一般位于“/sys/class/power_supply/xxx/”中)
- static struct device_attribute power_supply_attrs[] = {
- POWER_SUPPLY_ATTR(status),
- POWER_SUPPLY_ATTR(charge_type),
- POWER_SUPPLY_ATTR(health),
- POWER_SUPPLY_ATTR(present),
- POWER_SUPPLY_ATTR(online),
- POWER_SUPPLY_ATTR(authentic),
- POWER_SUPPLY_ATTR(technology),
- POWER_SUPPLY_ATTR(cycle_count),
- POWER_SUPPLY_ATTR(voltage_max),
- POWER_SUPPLY_ATTR(voltage_min),
- POWER_SUPPLY_ATTR(voltage_max_design),
- POWER_SUPPLY_ATTR(voltage_min_design),
- POWER_SUPPLY_ATTR(voltage_now),
- POWER_SUPPLY_ATTR(voltage_avg),
- POWER_SUPPLY_ATTR(voltage_ocv),
- POWER_SUPPLY_ATTR(current_max),
- POWER_SUPPLY_ATTR(current_now),
- POWER_SUPPLY_ATTR(current_avg),
- POWER_SUPPLY_ATTR(power_now),
- POWER_SUPPLY_ATTR(power_avg),
- POWER_SUPPLY_ATTR(charge_full_design),
- POWER_SUPPLY_ATTR(charge_empty_design),
- POWER_SUPPLY_ATTR(charge_full),
- POWER_SUPPLY_ATTR(charge_empty),
- POWER_SUPPLY_ATTR(charge_now),
- POWER_SUPPLY_ATTR(charge_avg),
- POWER_SUPPLY_ATTR(charge_counter),
- POWER_SUPPLY_ATTR(constant_charge_current),
- POWER_SUPPLY_ATTR(constant_charge_current_max),
- POWER_SUPPLY_ATTR(constant_charge_voltage),
- POWER_SUPPLY_ATTR(constant_charge_voltage_max),
- POWER_SUPPLY_ATTR(charge_control_limit),
- POWER_SUPPLY_ATTR(charge_control_limit_max),
- POWER_SUPPLY_ATTR(energy_full_design),
- POWER_SUPPLY_ATTR(energy_empty_design),
- POWER_SUPPLY_ATTR(energy_full),
- POWER_SUPPLY_ATTR(energy_empty),
- POWER_SUPPLY_ATTR(energy_now),
- POWER_SUPPLY_ATTR(energy_avg),
- POWER_SUPPLY_ATTR(capacity),
- POWER_SUPPLY_ATTR(capacity_alert_min),
- POWER_SUPPLY_ATTR(capacity_alert_max),
- POWER_SUPPLY_ATTR(capacity_level),
- POWER_SUPPLY_ATTR(temp),
- POWER_SUPPLY_ATTR(temp_alert_min),
- POWER_SUPPLY_ATTR(temp_alert_max),
- POWER_SUPPLY_ATTR(temp_ambient),
- POWER_SUPPLY_ATTR(temp_ambient_alert_min),
- POWER_SUPPLY_ATTR(temp_ambient_alert_max),
- POWER_SUPPLY_ATTR(time_to_empty_now),
- POWER_SUPPLY_ATTR(time_to_empty_avg),
- POWER_SUPPLY_ATTR(time_to_full_now),
- POWER_SUPPLY_ATTR(time_to_full_avg),
- POWER_SUPPLY_ATTR(type),
- POWER_SUPPLY_ATTR(scope),
- POWER_SUPPLY_ATTR(usb_hc),
- POWER_SUPPLY_ATTR(usb_otg),
- POWER_SUPPLY_ATTR(charge_enabled),
- POWER_SUPPLY_ATTR(charge_counter_ext),
- POWER_SUPPLY_ATTR(model_name),
- POWER_SUPPLY_ATTR(manufacturer),
- POWER_SUPPLY_ATTR(serial_number),
- };
最后从PSY driver的角度,说明一下怎么基于power supply class编写驱动:
(1)根据硬件spec,确定PSY设备具备哪些特性,并把他们和enum power_supply_property对应。
(2)根据实际情况,实现这些properties的get/set接口。
(3)定义一个struct power_supply 变量,并初始化必要字段后,调用power_supply_register或者power_supply_register_no_ws,将其注册到kernel中。
(4)根据实际情况,启动设备属性变化的监控逻辑,例如中断,轮询等,并在发生改变时,调用power_supply_changed,通知power suopply core。
power supply子系统的引入
以市面上一款常见的的平板方案来看一看,进入平板的sys/class/power_supply/目录下
可以看到这里有三个PSY设备,分别对应USB充电器 DC充电器,和电池。
进入battery目录下,发现下面有各种各样的属性,另外两个atc260x-usb 、atc260x-wall目录下分别也是这样。
那么在内核中肯定有三个驱动对应这个三个PSY设备。
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 noden", __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);
- //这里就是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设备,其编程方法也是类似的,这里不再赘述。
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 proben");
- 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, "%sn", __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, "%sn", __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中读取电池相关的属性信息,展现给用户空间。
/drivers/power/supply/qcom/qpnp-smb5.c charger driver
/drivers/power/supply/qcom/smb5-lib.c 依赖的SMB library
/arch/arm64/boot/dts/qcom/pmi632.dtsi dtsi
高通平台power_supply 框架下添加第三方充电IC的驱动方法
原文链接:https://blog.csdn.net/stoic163/article/details/99680422
1.power_supply电源框架介绍:power supply framework在kernel/drivers/power/下。内核抽象出来power supply子系统为驱动提供了统一的框架。
功能包括:
1.抽象PSY设备的共性,向用户空间提供统一的API;
2.为底层PSY驱动的编写,提供简单、统一的方式,同时封装并实现公共逻辑。
power supply class位于drivers/power/目录中,主要由3部分组成(可参考下图的软件架构):
1)power_supply_core,用于抽象核心数据结构、实现公共逻辑。位于drivers/power/power_supply_core.c中。
2)power_supply_sysfs,实现sysfs以及uevent功能。位于drivers/power/power_supply_sysfs.c中。
3)power_supply_leds,基于Linux led class,提供PSY设备状态指示的通用实现。位于drivers/power/power_suppply_leds.c中。
最后,驱动工程师可以基于power supply class,实现具体的PSY drivers,主要处理平台相关、硬件相关的逻辑。这些drivers都位于drivers/power/power_supply目录下。
在具体设备文件中在/sys/class/power_supply,具体如下:
power_supply 框架工作流程
Linux的设备文件目录中可以在sys/class/下看到power_supply目录;这个power_supply的类是通过power_supply_core.c文件中的power_supply_class_init()中的class_create()函数来进行power_supply类的创建,如下:
进入power_supply的目录下:
我们可以看到出现了battery与usb两个目录,这两个目录就是充电IC(battery)与USB(usb)电源管理部分的注册的设备节点内容的集合。
进入battery目录下:
可以看到很多设备节点,这些节点就是通过power_supply的电源框架提供的方法进行注册并实现其内容的。
对于上述的节点内容,是怎么实现的,其实power_supply都提供了相应的节点名称,在文件/kernel/include/linux/power_supply.h中提供了相关的属性枚举定义。如下:
这里提供了大部分的设备节点属性。作为驱动工程师,只需要选取芯片存在的并且自己需要的属性并实现其内容即可。
在power_supply的结构体中(也可能是在结构体之外):
在这个结构体中,可以看到有set_property与get_property两个属性的函数指针,这两个函数指针就是用来具体实现相关属性功能的。
power_supply_sysfy文件系统
power_supply_sysfs.c文件中,具体进行的是设备节点的注册过程,以及相关功能的实现:
从上面可以看出,在power_supply_sysfs文件中主要是实现power_supply_attrs数组中的成员的show与store。
先看power_supply_attrs数组:
从上述内容看,power_supply_attrs数组其实就是实现之前power_supply.h文件中的枚举power_supply_property。而POWER_SUPPLY_ATTR(online)中括号内容即是具体设备节点。
在POWER_SUPPLY_ATTR结构体中,有show与store两个指针。
show的内容就是使用cat xxxx节点的操作显示出来的内容的:
从上面可以看到有些节点显示的内容并不是数字,而是在show函数中被转换成了相关的字符串。如:
而store函数是echo xx > xxx节点的操作写入的内容。
在上述节点中,存在uevent这个节点,可以看看uevent节点的内容:
这个节点由power_supply_sysfs文件中的power_supply_uevent函数实现的。
这个函数的作用就是将该设备节点下所有节点的内容组合成字符串发送到uevent中,在通过内核的uevent框架发送到用户空间。
当设备节点内容发生变化时,会调用power_supply_changed函数:
可以看到该函数是将psy->changed_work加入到工作队列中,具体的调用设备的changed_work的工作队列,看看具体做什么:
可以看到,对该类中查询每个设备的更新操作,再更新led灯的操作,还有就是发送新的事件(uevent)通知应用层。
至此power_supply就介绍结束了。



