硬件平台:RK3128
Android版本:6.0
Kernel版本:3.10.92
Android电池管理,基于linux Power Supply架构,Battery驱动程序需要通过sys文件系统向用户空间提供接口,Linux标准的Power Supply驱动程序所使用的文件系统路径问/sys/class/power_supply,其中的每个子目录表示一种能源供应设备的名称。
一、Power Supply初始化
先来看看power_supply结构体,定义在include/linux/power_supply.h中。
struct power_supply {
const char *name;
/*名称*/
enum power_supply_type type;
/*类型*/
enum power_supply_property *properties;
/*属性*/
size_t num_properties;
/*属性数目*/ char **supplied_to;
size_t num_supplicants;
char **supplied_from;
size_t num_supplies;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif 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);
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);
/* For APM emulation, think legacy userspace. */
int use_for_apm;
/* private */
struct device *dev;
struct work_struct changed_work;
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;
char *online_trig_name;
struct led_trigger *charging_blink_full_solid_trig;
char *charging_blink_full_solid_trig_name;
#endif
};
初始化:
drivers/power/power_supply_core.c
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;
}
可以看到创建了class power_supply,也就是/sys/class/power_supply,接着绑定了dev_uevent,dev_uevent是结构体class的一个函数指针,定义在include/linux/device.h中,看看源码中的描述:
* @dev_uevent: Called when a device is added, removed from this class, or a
*few other things that generate uevents to add the environment
*variables.
也就是说每添加一个设备到power_supply中,就会调用一次power_supply_uevent。
注册:
int power_supply_register(struct device *parent, struct power_supply *psy)
{
struct device *dev;
int rc;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
device_initialize(dev);
dev->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);
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);
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);
goto success;
create_triggers_failed:
psy_unregister_cooler(psy);
register_cooler_failed:
psy_unregister_thermal(psy);
register_thermal_failed:
wakeup_init_failed:
device_del(dev);
kobject_set_name_failed:
device_add_failed:
check_supplies_failed:
put_device(dev);
success:
return rc;
}
再看看drivers/power/power_supply_sysfs.c
/* 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),
...
...
这里是所有的电源设备属性,但并不需要全部创建,需要创建哪些由我们注册时指定,把需要创建的属性放在properties中即可,在power_supply_uevent中很容易可以看到:
for (j = 0;
j < psy->num_properties;
j++) {
struct device_attribute *attr;
char *line;
attr = &power_supply_attrs[psy->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;
}
psy即我们传入的power_supply。
二、Battery driver
Battery driver通过power_supply_register注册到Power Supply core中,来看看源码:
data ->bat = rk30_battery_supply;
ret = power_supply_register(&pdev->dev,&data ->bat);
【Linux|Android 电池管理系统-驱动部分】rk30_battery_supply:
static enum power_supply_property rk30_adc_battery_props[] = { POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
};
static struct power_supply rk30_battery_supply =
{
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY, .get_property= rk30_adc_battery_get_property, .properties= rk30_adc_battery_props,
.num_properties = ARRAY_SIZE(rk30_adc_battery_props),
};
rk30_adc_battery_props即指定了需要创建的节点列表。到这里Battery驱动就创建完成了。
三、电量更新
Android电量更新是通过uevent通知上层,battery driver会不断计算当前电量,当电量发生变化后,会调用power_supply_changed(&bat ->bat);
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);
}
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,
changed_work);
dev_dbg(psy->dev, "%s\n", __func__);
spin_lock_irqsave(&psy->changed_lock, flags);
if (psy->changed) {
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);
kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
spin_lock_irqsave(&psy->changed_lock, flags);
}
if (!psy->changed)
pm_relax(psy->dev);
spin_unlock_irqrestore(&psy->changed_lock, flags);
}
可以看到changed_work中会调用kobject_uevent,其后流程如下:
kobject_uevent ->
kobject_uevent_env->
netlink_broadcast_filtered
通过netlink机制发送给framework用以更新电量,关于framework的部分会在下篇介绍。
推荐阅读
- Linux|109 个实用 shell 脚本
- linux笔记|linux 常用命令汇总(面向面试)
- Linux|Linux--网络基础
- linux|apt update和apt upgrade命令 - 有什么区别()
- linux|2022年云原生趋势
- Go|Docker后端部署详解(Go+Nginx)
- 开源生态|GPL、MIT、Apache...开发者如何选择开源协议(一文讲清根本区别)
- GitHub|7 款可替代 top 命令的工具