psy driver的主要功能,就是向用户空间程序汇整各类状态信息。
将这些状态信息,抽象为properties,由于状态信息的类型是有限的,properties的个数也是有限的。
psy driver只需要负责:该psy设备具有哪些“属性”;这些“属性”的value是什么;当“属性值”发生改变时,通知power supply class。
power supply class负责:将某个PSY设备支持的属性及其value,以sysfs的形式,提供给用户空间;当属性值改变时,以uevent的形式,广播给用户空间程序。
power supply class位于drivers/power/目录中,主要由3部分组成(可参考下图的软件架构):
power supply core,用于抽象核心数据结构、实现公共逻辑。位于drivers/power/power_supply_core.c中。
power supply sysfs,实现sysfs以及uevent功能。位于drivers/power/power_supply_sysfs.c中。
power supply leds,基于linux led class,提供PSY设备状态指示的通用实现。位于drivers/power/power_suppply_leds.c中。
最后,驱动工程师可以基于power supply class,实现具体的PSY drivers,主要处理平台相关、硬件相关的逻辑。这些drivers都位于drivers/power/目录下。
path:kernel-4.4/include/linux/power_supply.h
struct power_supply {
const struct power_supply_desc *desc;
char **supplied_to;//一个字符串数组,保存了由该PSY供电的PSY列表,以此可将PSY组织成相互级联的PSY链。这些“被供电”的PSY,称作supplicant(客户端、乞求者);
size_t num_supplicants;//supplicant的个数;
char **supplied_from;//一个字符串数组,保存了向该PSY供电的PSY列表,也称作supply(提供者)。从另一个方向,组织PSY之间的级联关系;
size_t num_supplies;//supply的个数;
struct device_node *of_node;//用于保存device、of_node等指针。
/* Driver private data */
void *drv_data;
/* private */
struct device dev;
struct work_struct changed_work;
struct delayed_work deferred_register_work;
spinlock_t changed_lock;
bool changed;
//一个用于处理状态改变的workqueue,主要思路是:当该PSY的状态发生改变,启动一个workqueue,查询并通知所有的supplicants;
atomic_t use_cnt;
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd;
struct thermal_cooling_device *tcd;
//如果该PSY具有温度等属性,则需要借助linux generic thermal sysfs drivers(温控子系统)的框架,注册相应的thermal设备
#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
};
power_supply_desc 结构体
struct power_supply_desc {
const char *name; //设备名称 /sys/class/power_supply/name
enum power_supply_type type; //电源类型
enum power_supply_property *properties; //供电属性
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);//get property
int (*set_property)(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val);
//PSY driver需要重点实现的两个回调函数,用于获取/设置属性值;
/*
* 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);//返回指定的属性值是否可写(用于sysfs);
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);//外部模块通知PSY driver,该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;
/* For APM emulation, think legacy userspace. */
int use_for_apm;
};
power_supply_type结构体
在mtk平台
enum power_supply_type {
POWER_SUPPLY_TYPE_UNKNOWN = 0,
POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
POWER_SUPPLY_TYPE_WIRELESS, /* Wireless Charger */
};
标准的linux
enum power_supply_type {
OWER_SUPPLY_TYPE_UNKNOWN = 0,
POWER_SUPPLY_TYPE_BATTERY,//电池,嵌入式设备、手持式智能设备常用的供电形式;
POWER_SUPPLY_TYPE_UPS,//Uninterruptible Power System/Uninterruptible Power Supply,不间断式供电设备,通过将交流电和蓄电池连接,正常情况下由交流电供电,同时向蓄电池充电。当交流电断电时,由蓄电池紧急供电。一般用于服务器等设备;
POWER_SUPPLY_TYPE_MAINS,//主供电设备,如笔记本电脑的适配器,其特点是可以单独供电,当其断电时,再由辅助供电设备供电(如battery);
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 */
};
即 enum power_supply_property
extern int power_supply_register(struct device *parent, struct power_supply *psy); //有wakeup system的能力
extern int power_supply_register_no_ws(struct device *parent,struct power_supply *psy); //无wakeup system的能力
extern void power_supply_unregister(struct power_supply *psy);
extern void power_supply_changed(struct power_supply *psy);
/**********************
当PSY driver检测到该设备某些属性值改变时,需要调用这个接口,通知power supply core,power supply core会有如下动作:
如果该PSY是其它PSY的供电源,调用这些PSY的external_power_changed回调函数,通知它们(这些PSY具体要做些什么,由它们的自身逻辑决定);
如果配置了CONFIG_LEDS_TRIGGERS,调用power_supply_update_leds,更新该PSY有关的led状态;
发送notifier,通知那些关心PSY设备状态的drivers;
以统一的格式,向用户空间发送uevent
***************************/
extern struct power_supply *power_supply_get_by_name(const char *name); //通过名字获取PSY指针。
extern struct power_supply *power_supply_get_by_phandle(struct device_node *np,const char *property); //从DTS中,解析出对应dePSY指针。
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_charged回调。
extern int power_supply_is_system_supplied(void); //查询系统是否有有效的或者处于online状态的PSY,如果没有,可能为桌面系统。
extern int power_supply_powers(struct power_supply *psy, struct device *dev);//,在指定设备(通常是该PSY设备)的sysfs目录(/sys/devices/xxx/)下,创建指定PSY的符号链接(/sys/devices/xxx/powers)。
extern int power_supply_reg_notifier(struct notifier_block *nb); //通过notifier注册接口注册notifier之后,系统任何PSY设备的状态发生改变,并调用了power_supply_changed接口,power supply core就是通知notifier的监听者。
extern void power_supply_unreg_notifier(struct notifier_block *nb);
POWER_SUPPLY_NAME=xxx /* power supply name */
POWER_SUPPLY_xxx1=xxx /* property = value */
POWER_SUPPLY_xxx2=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(voltage_boot),
POWER_SUPPLY_ATTR(current_max),
POWER_SUPPLY_ATTR(current_now),
POWER_SUPPLY_ATTR(current_avg),
POWER_SUPPLY_ATTR(current_boot),
POWER_SUPPLY_ATTR(power_now),
创建一个名为power_supply的class,挂上一个uevent,之后填充device_type
power_supply_core.c
static struct device_type power_supply_dev_type;
static int __init power_supply_class_init(void)
{
power_supply_class = class_create(THIS_MODULE, "power_supply");
if (IS_ERR(power_supply_class))
return PTR_ERR(power_supply_class);
power_supply_class->dev_uevent = power_supply_uevent;
power_supply_init_attrs(&power_supply_dev_type);
return 0;
}
subsys_initcall(power_supply_class_init);
add_uevent_var中添加发送msg到uevent,如battery,后面的for循环中是将power_supply_attrs中的对应的属性和值,以POWER_SUPPLY_XXXX=xxxx的方式添加到发送的数据中。这个回调函数就是做了这个操作,即将系统中的变化数据组织成特定的格式,通过uevent发送出去
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct power_supply *psy = dev_get_drvdata(dev);
int ret = 0, j;
char *prop_buf;
char *attrname;
dev_dbg(dev, "uevent\n");
if (!psy || !psy->desc) {
dev_dbg(dev, "No power supply yet\n");
return ret;
}
dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name);
ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
if (ret)
return ret;
prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
if (!prop_buf)
return -ENOMEM;
for (j = 0; j < psy->desc->num_properties; j++) {
struct device_attribute *attr;
char *line;
attr = &power_supply_attrs[psy->desc->properties[j]];
ret = power_supply_show_property(dev, attr, prop_buf);
if (ret == -ENODEV || ret == -ENODATA) {
/* When a battery is absent, we expect -ENODEV. Don't abort;
send the uevent with at least the the PRESENT=0 property */
ret = 0;
continue;
}
if (ret < 0)
goto out;
line = strchr(prop_buf, '\n');
if (line)
*line = 0;
attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
if (!attrname) {
ret = -ENOMEM;
goto out;
}
dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
kfree(attrname);
if (ret)
goto out;
}
out:
free_page((unsigned long)prop_buf);
return ret;
}
填充sysfs
static struct attribute_group power_supply_attr_group = {
.attrs = __power_supply_attrs,
.is_visible = power_supply_attr_is_visible,
};
static const struct attribute_group *power_supply_attr_groups[] = {
&power_supply_attr_group,
NULL,
};
void power_supply_init_attrs(struct device_type *dev_type)
{
int i;
dev_type->groups = power_supply_attr_groups;
for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
__power_supply_attrs[i] = &power_supply_attrs[i].attr;
}
POWER_SUPPLY_ATTR
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),
......
在static int __init battery_probe(struct platform_device *dev)中
battery_main.psy = power_supply_register(&(dev->dev), &battery_main.psd, NULL);
battery_main
static struct battery_data battery_main = {
.psd = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = battery_props,
.num_properties = ARRAY_SI
ZE(battery_props),
.get_property = battery_get_property,
},
.BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING,
.BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD,
.BAT_PRESENT = 1,
.BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION,
.BAT_CAPACITY = -1,
.BAT_batt_vol = 0,
.BAT_batt_temp = 0,
/* Dual battery */
.status_smb = POWER_SUPPLY_STATUS_DISCHARGING,
.capacity_smb = 50,
.present_smb = 0,
/* ADB CMD discharging */
.adjust_power = -1,
};
battery_data
struct battery_data {
struct power_supply_desc psd;
struct power_supply *psy;
int BAT_STATUS;
int BAT_HEALTH;
int BAT_PRESENT;
int BAT_TECHNOLOGY;
int BAT_CAPACITY;
/* Add for Battery Service */
int BAT_batt_vol;
int BAT_batt_temp;
/* Add for EM */
int BAT_TemperatureR;
int BAT_TempBattVoltage;
int BAT_InstatVolt;
int BAT_BatteryAverageCurrent;
int BAT_BatterySenseVoltage;
int BAT_ISenseVoltage;
int BAT_ChargerVoltage;
/* Dual battery */
int status_smb;
int capacity_smb;
int present_smb;
int adjust_power;
};
填充battery_get_property 回调函数
static int battery_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
int ret = 0;
int fgcurrent = 0;
bool b_ischarging = 0;
struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = data->BAT_STATUS;
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = data->BAT_HEALTH;/* do not change before*/
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = data->BAT_PRESENT;/* do not change before*/
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = data->BAT_TECHNOLOGY;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = data->BAT_CAPACITY;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
b_ischarging = gauge_get_current(&fgcurrent);
if (b_ischarging == false)
fgcurrent = 0 - fgcurrent;
val->intval = fgcurrent / 10;
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
val->intval = 3000000;
/* 3A */
break;
.......
添加一个新的节点主要就是填充sysfs以及battery_get_property
power_supply_sysfs.c
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),
.......
#if 1
POWER_SUPPLY_ATTR(BatteryVoltage),
POWER_SUPPLY_ATTR(FGVoltage),
POWER_SUPPLY_ATTR(BatteryCurrent),
POWER_SUPPLY_ATTR(FGCurrent),
POWER_SUPPLY_ATTR(BatInstTemp),
POWER_SUPPLY_ATTR(BasSoc),
POWER_SUPPLY_ATTR(PreSoc),
POWER_SUPPLY_ATTR(BoardTemp),
POWER_SUPPLY_ATTR(CPUTemp),
#endif
......
填充battery_get_property
static int battery_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
int ret = 0;
int fgcurrent = 0;
bool b_ischarging = 0;
struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = data->BAT_STATUS;
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = data->BAT_HEALTH;/* do not change before*/
break;
填充battery_get_property首先需要填充case 也就是enum power_supply_property 即相应properties
static enum power_supply_property battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
......
POWER_SUPPLY_PROP_BatteryVoltage,
POWER_SUPPLY_PROP_FGVoltage,
POWER_SUPPLY_PROP_BatteryCurrent,
POWER_SUPPLY_PROP_FGCurrent,
POWER_SUPPLY_PROP_BatInstTemp,
POWER_SUPPLY_PROP_BasSoc,
POWER_SUPPLY_PROP_PreSoc,
POWER_SUPPLY_PROP_BoardTemp,
POWER_SUPPLY_PROP_CPUTemp,
.......
之后填充battery_data的值
struct battery_data *data = container_of(psy->desc, struct battery_data,
struct battery_data {
struct power_supply_desc psd;
struct power_supply *psy;
int BAT_STATUS;
int BAT_HEALTH;
int BAT_PRESENT;
int BAT_TECHNOLOGY;
......
int BatteryVoltage;
int FGVoltage;
int BatteryCurrent;
int FGCurrent;
int BatInstTemp;
int BasSoc;
int PreSoc;
int BoardTemp;
int CPUTemp;
.....
填充完毕后如下
static int battery_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
int ret = 0;
int fgcurrent = 0;
bool b_ischarging = 0;
struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = data->BAT_STATUS;
.......
case POWER_SUPPLY_PROP_BatteryVoltage:
val->intval = data->BatteryVoltage;
break;
case POWER_SUPPLY_PROP_FGVoltage:
val->intval = data->FGVoltage;
break;
case POWER_SUPPLY_PROP_BatteryCurrent:
val->intval = data->BatteryCurrent;
break;
case POWER_SUPPLY_PROP_FGCurrent:
val->intval = data->FGCurrent;
break;
case POWER_SUPPLY_PROP_BatInstTemp:
val->intval = data->BatInstTemp;
break;
case POWER_SUPPLY_PROP_BasSoc:
val->intval = data->BasSoc;
break;
case POWER_SUPPLY_PROP_PreSoc:
val->intval = data->PreSoc;
break;
case POWER_SUPPLY_PROP_BoardTemp:
val->intval = data->BoardTemp;
break;
case POWER_SUPPLY_PROP_CPUTemp:
val->intval = data->CPUTemp;
break;
.......
最后需要去实现,想get的value
如在fg_drv_update_hw_status 线程中定期更新这些value
void fg_drv_update_hw_status(void)
{
static bool fg_current_state;
signed int chr_vol;
int fg_current, fg_coulomb, bat_vol, plugout_status, tmp, bat_plugout_time;
int fg_current_iavg;
bool valid = false;
static unsigned int cnt;
struct battery_data *bat_data = &battery_main;
static signed int previous_SOC = -1;
...........
bat_data->BatteryVoltage = bat_vol; // 瞬间电压
bat_data->FGVoltage = pmic_get_battery_voltage(); // FG电压
bat_data->BatteryCurrent = fg_current_iavg; // 瞬时充电电流
bat_data->FGCurrent = fg_current; // FG电流
bat_data->BatInstTemp = battery_meter_get_battery_temperature(); // 瞬间电池温度
bat_data->BasSoc = battery_get_bat_soc(); // 平均SOC
bat_data->PreSoc = previous_SOC; // 前SOC
bat_data->CPUTemp = mtk_thermal_get_temp(MTK_THERMAL_SENSOR_CPU); // cpu 温度
bat_data->BoardTemp = mtk_thermal_get_temp(MTK_THERMAL_SENSOR_AP); // board 温度
...........
参考:http://www.wowotech.net/pm_subsystem/psy_class_overview.html