1. 前言
power supply class为编写供电设备(power supply,后面简称PSY)的驱动提供了统一的框架,功能包括:
1)抽象PSY设备的共性,向用户空间提供统一的API。
2)为底层PSY驱动的编写,提供简单、统一的方式。同时封装并实现公共逻辑,驱动工程师只需把精力集中在和硬件相关的部分即可。
本文将从设计思路、软件架构、API说明以及怎么编写power supply driver四个角度,介绍power supply class。并会在下一篇文章中,分析power supply class的内部逻辑。如果有时间,会在第三篇文章中,以android系统为例,介绍应用软件怎样利用power supply class,监控系统的供电状态。
注:其实所有的class(如input subsystem),思路都是这样的----抽象共性、统一接口、屏蔽细节。我们在“Linux设备模型(7)_Class”中介绍过,本文在介绍power supply class同时,也以此为例,进一步理解设备模型中class的存在意义和使用方法。
2. 设计思路
先来回答一个问题:kernel中设备驱动的目的,是管理设备,并提供给用户空间程序使用,那么对PSY设备而言,kernel要管理什么?用户空间程序要使用什么?
其实PSY设备是一个特例,它的目的很单纯,就是为系统供电。如果只考虑这个目的,就不需要任何驱动了,但情况会稍微复杂,因为:
1)PSY设备可能是电池设备(battery,这在嵌入式系统中很常见),这会引申出电量检测、充电管理等多个议题。
此时,PSY driver需要管理的事情包括:检测电池类型;检测电池电量;检测充电状态;等等。而用户空间程序则需要将检测到的结果,显示给用户。
2)系统中可能有多个PSY设备,这些设备还可能有级联关系,如有些平板电脑中,可能同时存在DC-charger、USB-charger和battery三个供电设备,其中DC-charger和USB-charger可能会给battery供电,再由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. 软件架构和API汇整
3.1 软件架构
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/目录下。
3.2 核心数据结构
1)struct power_supply
struct power_supply为power supply class的核心数据结构,用于抽象PSY设备。其定义如下:
1: /* include/linux/power_supply.h */
2: struct power_supply {
3: const char *name;
4: enum power_supply_type type;
5: enum power_supply_property *properties;
6: size_t num_properties;
7:
8: char **supplied_to;
9: size_t num_supplicants;
10:
11: char **supplied_from;
12: size_t num_supplies;
13: struct device_node *of_node;
14:
15: int (*get_property)(struct power_supply *psy,
16: enum power_supply_property psp,
17: union power_supply_propval *val);
18: int (*set_property)(struct power_supply *psy,
19: enum power_supply_property psp,
20: const union power_supply_propval *val);
21: int (*property_is_writeable)(struct power_supply *psy,
22: enum power_supply_property psp);
23: void (*external_power_changed)(struct power_supply *psy);
24: void (*set_charged)(struct power_supply *psy);
25:
26: /* For APM emulation, think legacy userspace. */
27: int use_for_apm;
28:
29: /* private */
30: struct device *dev;
31: struct work_struct changed_work;
32: spinlock_t changed_lock;
33: bool changed;
34: #ifdef CONFIG_THERMAL
35: struct thermal_zone_device *tzd;
36: struct thermal_cooling_device *tcd;
37: #endif
38:
39: #ifdef CONFIG_LEDS_TRIGGERS
40: struct led_trigger *charging_full_trig;
41: char *charging_full_trig_name;
42: struct led_trigger *charging_trig;
43: char *charging_trig_name;
44: struc