概述
对于便携式设备,如手机或者pad来说,battery都是必不可少的一个组成部分。kernel中为了方便对battery的管理,专门提供了power supply framework。battery管理我觉得可以分开为两个部分,一个是电池监控(fuelgauge),另一个是充放电管理(charger),所以我们在内核中也是把它分成了两个驱动来管理。fuelgauge驱动主要是负责向上层android系统提供当前电池的电量以及健康状态信息等等,另外除了这个以外,它也向charger驱动提供电池的相关信息;charger驱动主要负责电源线的插拔检测,以及充放电的过程管理。对于battery管理,硬件上有电量计IC和充放电IC,当然有些厂家为了成本的考虑,也会把电量计和充放电功能集成到一个IC上,更有甚者,可能会把PMU功能也集成在一块硅面上,actions就有相关方案。
power supply 框架
内核中有power manager(PM)子系统和power supply子系统,这两者负责的内容是不同的,PM子系统是系统电源管理策略的解决方案,更多的关注功耗控制。
1.编译选项
power supply framework所在的内核目录如下:
kernel/drivers/power
首先来看一下power目录下的Makefile和Kconfig:
Makefile:
1 ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG
2
3 power_supply-y := power_supply_core.o
4 power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o
5 power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
6
7 obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
Kconfig:
1 menuconfig POWER_SUPPLY
2 bool "Power supply class support"
3 help
4 Say Y here to enable power supply class support. This allows
5 power supply (batteries, AC, USB) monitoring by userspace
6 via sysfs and uevent (if available) and/or APM kernel interface
7 (if selected below).
由上面的编译选项可以看出,要想使能power supply功能,我们需要在kernel defconfig中定义CONFIG_POWER_SUPPLY功能,主要编译的文件是power_supply_core.c,如果内核中还配置了SYSFS和LEDS_TRIGGERS,那么power_supply_sysfs.c和power_supply_leds.c也会一起编译到内核中。
2.power supply core
内核抽象了一个供电设备为struct power_supply,定义在kernel/include/linux/power_supply.h头文件中:
235 struct power_supply {
236 const char *name;
237 enum power_supply_type type;
238 enum power_supply_property *properties;
239 size_t num_properties;
240
241 char **supplied_to;
242 size_t num_supplicants;
243
244 char **supplied_from;
245 size_t num_supplies;
246 #ifdef CONFIG_OF
247 struct device_node *of_node;
248 #endif
249
250 int (*get_property)(struct power_supply *psy,
251 enum power_supply_property psp,
252 union power_supply_propval *val);
253 int (*set_property)(struct power_supply *psy,
254 enum power_supply_property psp,
255 const union power_supply_propval *val);
256 int (*property_is_writeable)(struct power_supply *psy,
257 enum power_supply_property psp);
258 void (*external_power_changed)(struct power_supply *psy);
259 void (*set_charged)(struct power_supply *psy);
260
261 /* For APM emulation, think legacy userspace. */
262 int use_for_apm;
263
264 /* private */
265 struct device *dev;
266 struct work_struct changed_work;
267 spinlock_t changed_lock;
268 bool changed;
269 struct wake_lock work_wake_lock;
270 #ifdef CONFIG_THERMAL
271 struct thermal_zone_device *tzd;
272 struct thermal_cooling_device *tcd;
273 #endif
274
275 #ifdef CONFIG_LEDS_TRIGGERS
276 struct led_trigger *charging_full_trig;
277 char *charging_full_trig_name;
278 struct led_trigger *charging_trig;
279 char *charging_trig_name;
280 struct led_trigger *full_trig;
281 char *full_trig_name;
282 struct led_trigger *online_trig;
283 char *online_trig_name;
284 struct led_trigger *charging_blink_full_solid_trig;
285 char *charging_blink_full_solid_trig_name;
286 #endif
287 };
核心层代码实现了一个power_supply_class,用于抽象power_supply设备,这样有助于设备模型的统一,并且针对该class,可以在core中做统一处理,也方便于对同种类型设备的管理。
power_supply向外提供了接口以供具体驱动来使用,主要接口如下:
309 extern struct power_supply *power_supply_get_by_name(const char *name);
310 extern void power_supply_changed(struct power_supply *psy);
311 extern int power_supply_am_i_supplied(struct power_supply *psy);
312 extern int power_supply_set_battery_charged(struct power_supply *psy);
313 extern int power_supply_set_current_limit(struct power_supply *psy, int limit);
314 extern int power_supply_set_online(struct power_supply *psy, bool enable);
315 extern int power_supply_set_present(struct power_supply *psy, bool enable);
316 extern int power_supply_set_scope(struct power_supply *psy, int scope);
317 extern int power_supply_set_charge_type(struct power_supply *psy, int type);
318 extern int power_supply_set_supply_type(struct power_supply *psy,
319 enum power_supply_type supply_type);
320 extern int power_supply_is_system_supplied(void);
321 extern int power_supply_register(struct device *parent,
322 struct power_supply *psy);
323 extern void power_supply_unregister(struct power_supply *psy);
324 extern int power_supply_powers(struct power_supply *psy, struct device *dev);
上面的register和unregister接口是用来注册和反注册power supply设备的接口,当该power supply设备中有监测信息变化时,使用 power_supply_changed接口用来通知上层的android服务,这种通知机制采用的是netlink socket接口来实现的,具体实现参见该函数体。最终函数会通过netlink socket发送出来一系列的字符串,比如“POWER_SUPPLY_NAME=battery”,当上层daemon服务程序收到该提醒后,就会去读取相应驱动提供的sysfs接口文件来获取相应的信息。
3.power supply sysfs
power supply子系统定义了一系列的sysfs文件,这样做也是为了power supply设备的标准化。虽然power_supply_class定义的一系列sysfs文件,但是它们并不全部显示出来,而是通过不同的struct power_supply结构体来决定显示哪些sysfs节点。
238 enum power_supply_property *properties;
239 size_t num_properties;
这两个成员是用来定义特定power supply设备所具有的属性的,也就是对应着sysfs节点文件。另外power supply设备还要定义get_property和set_property回调函数,用来把相关的设备信息传递给上层。
4.power supply led
对于power supply led功能,比较简单,framework只负责提供几个API以供其他驱动调用:
31 extern void power_supply_update_leds(struct power_supply *psy);
32 extern int power_supply_create_triggers(struct power_supply *psy);
33 extern void power_supply_remove_triggers(struct power_supply *psy);
从函数名字可以看出来,这几个API分别就是创建和删除led_trigger,以及触发led。这几个API的实现基于内核LED框架,我们在power_supply驱动中如果要使能LED显示功能,首先需要定义power_supply结构体中的一系列LED触发器的名字,然后调用power_supply_create_triggers来创建相应名称的led_trigger。那么这个trigger name该如何定义呢?
从led子系统的相关实现来说起,led子系统中把led抽象成了两种设备,分别是led设备和led trigger设备,led设备主要关注于如何操作LED使其亮或者灭,而led trigger顾名思义就是触发器,这两种设备是相互关联的,一个led设备可以对应多个触发器,一个led trigger设备也可以对应多个led设备。在power supply中我们仅仅创建了一个led trigger,那么要想将它和led设备关联起来,就需要一个led设备,并且该led的设备的default trigger名字要设置为该power supply相应的名字,由此在注册led trigger时才能将两者关联起来。那么在接下来的对led trigger的控制操作中,才会最终操作到对应的led设备上去。
fuelgauge & charger driver
通过对上面power supply的介绍,可以对内核中的框架用有个大致的了解,那么如何去编写fuelgauge和charger驱动呢?
其实内核只是提供了一个框架,我们通过相关的API接口,可以生成相同的sysfs文件,内核仅仅是定义了这些统一的模型以及一些标准的功能,那对于单个驱动,我们更关注的是如何驱动特定硬件来实现这些标准的功能。虽然各个环境都有不同,但是不管每个环境的硬件是什么,我们都需要根据datasheet,把硬件的功能封装起来,并最终抽象到一个power supply结构体中,然后注册到内核框架层中,由此来完成驱动。