1. linux电源管理子系统用于管理系统的充放电以及供电.不同设备供电方式多样,即使是一个
设备也可以支持多个供电方式,例如一个平板可以使用usb,dc,电池供电.
电源管理子系统用于抽象这些供电,对上层它提供统一的操作接口,方便上层的编码.对下层
它实现了充电管理的共有逻辑以及sysfs接口的创建,驱动程序只需要按照规则实现底层接口
就可以了,简化了驱动程序的设计.
2. 电源管理的结构
一个struct power_supply 实例代表一个供电设备,比如一个usb供电设备,电池供电
设备. 通常用户不需要自己定义一个struct power_supply实例,当我们调用power_supply_register()
函数时函数内部会动态创建一个struct power_supply 实例.看下power_supply_register()
函数
/**
power_supply_register() - 注册新的电源供应设备
@parent: 电源供应设备的父设备,通常是调用此函数的探测函数所属的设备
@desc: 电源供应的描述信息,必须在此电源供应的整个生命周期内保持有效
@cfg: 注册过程中访问的运行时特定配置,可能为NULL
返回值:成功时返回新分配的power_supply的指针,否则返回ERR_PTR。
使用返回的power_supply指针调用power_supply_unregister()来释放资源。
*/
struct power_supply *__must_check power_supply_register(struct device *parent,
const struct power_supply_desc *desc,
const struct power_supply_config *cfg)
该函数有两个重要的参数struct power_supply_desc和struct power_supply_config.
struct power_supply_desc是我们初始化的重点.从名字可知,它是描述一个供电设备.不同供电设备
提供不同的功能,这种差异就是在这里体现的.
power_supply_desc:
/* Description of power supply */
struct power_supply_desc {
const char *name; //名字,这个名字会体现在/sys/class/xxx 下
enum power_supply_type type; //供电类型,例如常用的POWER_SUPPLY_TYPE_BATTERY,
enum power_supply_usb_type *usb_types; //usb类型
size_t num_usb_types;
enum power_supply_property *properties; //指向power supply 所有的属性
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()).
*/
/*
为实现电源供应类的驱动程序提供的函数。
这些函数不应被其他驱动程序直接调用以访问此电源供应。相反,应使用power_supply_*()函数(例如power_supply_get_property())来访问。
*/
int (*get_property)(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val);
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;
/* For APM emulation, think legacy userspace. */
int use_for_apm;
};
linux电源管理子系统是如何抽象一个供电设备的? 它把一个供电设备抽象成众多的属性,定义
在enum power_supply_property 枚举类型中, 例如一个电池供电设备它有最大电压,最大电流
当前电压,健康状况等等.power_supply_desc->properties参数表明设备支持的属性,设备注册成功
后这些属性就会一一对应到/sys/power/supply/name/xxx下的一个文件.应用层可以读写这些文件
来获取或设置供电设备的属性值.power_supply_desc->get_property,和power_supply_desc->set_property
就是驱动里获取和设置这些属性的实现接口.
struct power_supply_config
power_supply_config的内容很少,一般保留设备的私有数据.
如何基于电源管理子系统注册一个供电设备?
1. 定义个power_supply_desc实例,
2. 初始化power_supply_desc->properties 定义供电设备支持的属性
3. 实现这些属性的get_property, set_property底层实现接口.
4. 定义一个power_supply_config实例,指定一些设备的私有数据.
5. 调用power_supply_register()函数注册供电设备.
下面是已一个电池供电设备为例定实现的一个伪代码:
static enum power_supply_property mybat_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
};
static int mybat_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct mybat_data *data = power_supply_get_drvdata(psy);
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = data->xxx
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = data->xxx
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = data->xxx
break;
....
default:
ret = -EINVAL;
break;
}
return ret;
}
static int probe(struct platform_device *pdev)
{
/*定义一个实例*/
struct power_supply_desc *battery_desc = devm_kzalloc(&pdev->dev,
sizeof(struct power_supply_desc), GFP_KERNEL);
if (battery_desc == NULL) {
//error
}
struct power_supply_config battery_cfg = {};
battery_desc->properties = mybat_battery_props; //设备支持的属性
battery_desc->num_properties = ARRAY_SIZE(mybat_battery_props); //属性数量
battery_desc->get_property = mybat_battery_get_property; //获取属性
battery_desc->set_property = mybat_battery_set_property; //设置属性
battery_desc->property_is_writeable =
mybat_battery_property_is_writeable;
battery_desc->name = "battery"; //名字,会出现在/sys/class/powersupply/xxx
battery_desc->type = POWER_SUPPLY_TYPE_BATTERY;
battery_desc->no_thermal = true;
battery_cfg.drv_data = struct mybat_data; //定义私有数据
power_supply_register(&pdev->dev, battery_desc, &battery_cfg);
/*注册完成后会创建爱你/sys/class/power_supply/battery目录,并在目录下有
mybat_battery_props定义的这些属性文件.应用层访问这些属性文件获取电池属性.*/
}
驱动通知应用:
当电池状态发生变化时可以通过power_supply_changed()函数通知上层. 上层的uevent 守护进程
可以监听到这种变化.