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_supply
struct 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
设备的状态改变了,自己改变了自己不知道,要外部通知,希望大家在实际工作中不要遇到,太纠结了。
-
/* For APM emulation, think legacy userspace. */
-
int
use_for_apm;
-
/* private */
-
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
-
};
2.PSY
的类型
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,
/* Standard Downstream Port */
-
POWER_SUPPLY_TYPE_USB_DCP,
/* Dedicated Charging Port */
-
POWER_SUPPLY_TYPE_USB_CDP,
/* Charging Downstream Port */
-
POWER_SUPPLY_TYPE_USB_ACA,
/* Accessory Charger Adapters */
-
};
3.PSY
属性
power supply class
将所有可能
PSY
属性
(power_supply.h)
,以枚举型变量形式抽象出来,
PSY driver
可以根据设备的实际情况,从中选取一些。
-
enum power_supply_property {
-
/* Properties of type `int’ */
-
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, /* in percents! */
-
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
-
POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
-
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, /* use power_supply.type instead */
-
POWER_SUPPLY_PROP_SCOPE,
-
/* Local extensions */
-
POWER_SUPPLY_PROP_USB_HC,
-
POWER_SUPPLY_PROP_USB_OTG,
-
POWER_SUPPLY_PROP_CHARGE_ENABLED,
-
/* Local extensions of type int64_t */
-
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
-
/* Properties of type `const char *’ */
-
POWER_SUPPLY_PROP_MODEL_NAME,
-
POWER_SUPPLY_PROP_MANUFACTURER,
-
POWER_SUPPLY_PROP_SERIAL_NUMBER,
-
};
4.
向具体的PSY driver提供的API
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 name */
-
POWER_SUPPLY_xxx1=xxx
/* property = value */
-
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/”
中)
-
/* Must be in the same order as POWER_SUPPLY_PROP_* */
-
static
struct
device_attribute power_supply_attrs[] = {
-
/* Properties of type `int’ */
-
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),
-
/* Local extensions */
-
POWER_SUPPLY_ATTR(usb_hc),
-
POWER_SUPPLY_ATTR(usb_otg),
-
POWER_SUPPLY_ATTR(charge_enabled),
-
/* Local extensions of type int64_t */
-
POWER_SUPPLY_ATTR(charge_counter_ext),
-
/* Properties of type `const char *’ */
-
POWER_SUPPLY_ATTR(model_name),
-
POWER_SUPPLY_ATTR(manufacturer),
-
POWER_SUPPLY_ATTR(serial_number),
-
};
6.
怎样基于power supply class编写PSY driver
最后从
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 node\n”, __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);
-
/*init battery power supply*/ //
这里就是
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 probe\n”);
-
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, “%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
); //
将
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, “%s\n”, __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
就介绍结束了。