Linux 内核中为了方便对 battery 的管理,专门提供了power supply framework。battery 管理分开为两个部分,一个是电池监控(fuelgauge),另一个是充放电管理(charger)。
fuelgauge 驱动主要负责向上层 android 系统提供当前电池的电量以及健康状态信息等,另外它也向 charger 驱动提供电池的相关信息;
charger 驱动主要负责电源线的插拔检测,以及充放电的过程管理。对于 battery 管理,硬件上有电量计 IC 和充放电 IC。
power supply 设备,它的目的很单纯,就是为系统供电,但由于供电设备可以来自于电池,也可能来自于USB 供电、电源适配器供电、无线供电等等,这就涉及到充放电管理。另外,内核还需要导出必要的信息到用户空间,进而可告知用户程序,比如电池电量变化、充放电状态、电池类型等等。因此内核抽象出了 power supply 作为基础组件支撑。
所以 power supply 设备主要功能,就是向用户空间汇报各类状态信息,因此 power supply framework 的核心思路就是:将这些状态信息,抽象为properties——属性。由于状态信息类型是有限的,所以属性的个数也是有限的。
power supply 设备驱动只需要负责该 power supply 设备具备有哪些属性?这些属性的值是什么?当属性值发生改变时,通知 power supply framework。将某个power supply 设备支持的属性和对应值,以 sysfs 的形式提供给用户空间,当属性值改变时,以 uevent 的形式广播给用户空间程序。另外,power supply framework 也会协助处理power supply 设备级联的情况。
一、Power Supply 软件框架
power supply framework 源码存放在 drivers/power/ 路径下。内核抽象出来 power supply 子系统为驱动提供了统一的框架。功能包括:
- 抽象 power supply 设备的共性,向用户空间提供统一的 API;
- 为 power supply 设备驱动的编写提供简单、统一的方式。
power supply framework 主要由3部分组成:
- power supply core,通用电源监控类,用于抽象核心数据结构、实现公共逻辑。位于 drivers/power/power_supply_core.c 中。
- power supply sysfs,通用电源监控类的 Sysfs 接口,实现 Sysfs 以及 uevent 功能。位于 drivers/power/power_supply_sysfs.c 中。
- power supply leds,基于 Linux LED class,提供 power supply 设备状态指示的通用实现。位于 drivers/power/power_suppply_leds.c 中。
最后,驱动工程师可以基于 power supply framework,实现具体的 power supply 设备驱动程序,用来处理平台相关、硬件相关的逻辑。这些驱动都位于 drivers/power/ 目录下。
二、Power Supply 关键数据结构
power_supply_config 结构体代表运行时特定的 power supply 配置。
of_node:严格说,此数据结构不是设备模型中的,它是一个 DTS 中节点对应的内存中设备描述,一般此对象代表一个设备。这里代表 power supply 设备。
fwnode:fwnode 指的是一个固件节点,通常代表设备树或 ACPI(通常是 DSDT 表)中的一个条目。设备树和 ACPI 是定义设备及其属性和设备之间互连的两种不同方式。它们都使用树形结构来编码这一信息。
在一个给定的结构设备上的 fwnode 成员是该设备对应的固件表中的节点。ACPI 在基于 x86/UEFI 的系统中很常见,而设备树在 ARM 系统中很常见。
fwnode 可以与接受 fwnode 句柄的内核 api 一起使用。
supplied_to:一个字符串数组,保存了由该 power supply 设备供电的 power supply 设备列表,以此可将 power supply 设备组织成相互级联的 power supply 设备链。这些“被供电”的 power supply 设备,称作 supplicant(客户端、乞求者)。
num_supplicants:supplicant 数量。
power_supply_desc 结构体代表 power supply 说明(详细)。
name:设备名称。
type:设备类型。
usb_types:支持的 USB 类型(TYPE C 接口、专用的充电端口、充电下游端口等)。
num_usb_types:支持的 USB 类型数量。
properties:设备的属性列表。
num_properties:属性的个数。
get_property/set_property:用于驱动程序实现 power supply class 的函数。这些不应该被其他驱动程序直接调用来访问这个 power supply。取而代之的是使用 power_supply_*() 函数(例如 power_supply_get_property())。
property_is_writeable:返回指定的属性值是否可写(用于 Sysfs),它将在注册 power supply 设备时被调用。如果在设备探测期间发生这种情况,那么它一定不能访问设备的内部数据(因为探测没有结束)。
external_power_changed:当一个 power supply 设备存在 supply 设备,且该 power supply 设备的属性发生改变(如online、offline)时,power supply core 会调用该回调函数,通知 power supply 设备 driver,以便让它做出相应的处理。
set_charged:外部模块通知 power supply 设备 driver,该 power supply 设备的状态改变了。
no_thermal:是否为 power supply 设备创建 thermal zone。
use_for_apm:For APM emulation。
power_supply 结构体是 power supply class 的核心数据结构,用于抽象 power supply 设备。
desc:power_supply_desc 结构。
supplied_to:一个字符串数组,保存了由该 power supply 设备供电的 power supply 设备列表,以此可将 power supply 设备组织成相互级联的 power supply 设备链。这些“被供电”的 power supply 设备,称作 supplicant(客户端、乞求者)。
num_supplicants:supplicant 个数。
supplied_from:一个字符串数组,保存了向该 power supply 设备供电的 power supply 设备列表,也称作 supply(提供者)。从另一个方向,组织 power supply 设备之间的级联关系。
num_supplies:supply 的个数。
of_node:power supply 设备(它是 DTS 中的一个节点)。
dev:power supply 设备。
changed_work/changed_lock/changed:用于处理状态改变的 workqueue,主要思路是当该 power supply 设备的状态发生改变,提交 changed_work 到 workqueue,进而查询并通知所有的 supplicants。
deferred_register_work:power supply 注册结束时调用,将任务推迟执行。该工作队列里拥有一个 timer 定时器结构体,从而实现延时工作。
initialized:power supply 设备是否初始化完毕。
removing:power supply 设备正在移除中。
use_cnt:使用者计数。
tzd/tcd:如果该 power supply 设备具有温度等属性,则需要借助 Linux Generic Thermal Sysfs Drivers(温控子系统)的框架,注册相应的 Thermal 设备。
led_trigger:如果配置了 CONFIG_LEDS_TRIGGERS,则调用 Linux Led Class 的接口,注册相应的 LED 设备,用于power supply 设备状态指示。
include/linux/power_supply.h
/* Run-time specific power supply configuration */ struct power_supply_config {
struct device_node *of_node; struct fwnode_handle *fwnode; /* Driver private data */ void *drv_data; char **supplied_to; size_t num_supplicants; }; /* Description of power supply */ struct power_supply_desc {
const char *name; enum power_supply_type type; enum power_supply_usb_type *usb_types; size_t num_usb_types; 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); 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; }; struct power_supply {
const struct power_supply_desc *desc; char **supplied_to; size_t num_supplicants; char **supplied_from; size_t num_supplies; struct device_node *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; bool initialized; bool removing; atomic_t use_cnt; #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 };
讯享网
power supply framework 将所有可能 power supply 设备属性,以枚举(enum power_supply_property )的形式抽象出来,power supply 设备 driver 可以根据设备的实际情况,从中选取一些。
POWER_SUPPLY_PROP_STATUS,该 power supply 设备的 status,主要是充电状态,包括“Unknown”, “Charging”,“Discharging”, “Not charging”,“Full”,由枚举变量(POWER_SUPPLY_STATUS_*)定义。根据设计方案的不同,充电类型的 power supply 设备,或者 battery 类型的 power supply 设备,都可能具备该属性。
POWER_SUPPLY_PROP_CHARGE_TYPE,充电类型,包括:“Unknown”, “N/A”, “Trickle”,“Fast”,由枚举型变量(POWER_SUPPLY_CHARGE_TYPE_*)定义,同理根据设计方案的不同,充电类型的 power supply 设备,或者 battery 类型的 power supply 设备,都可能具备该属性。
POWER_SUPPLY_PROP_HEALTH,“健康”情况,包括“Unknown”,“Good”,“Overheat”,“Dead”,“Over voltage”等,由枚举型变量(POWER_SUPPLY_HEALTH_*)定义。一般用于 battery 类型的 power supply 设备。
POWER_SUPPLY_PROP_TECHNOLOGY,采用的技术,包括“Unknown”,“NiMH”,“Li-ion”,“Li-poly”, “LiFe”, “NiCd”,“LiMn”,由枚举型变量(POWER_SUPPLY_TECHNOLOGY_*)定义。一般用于 battery 类型的 power supply 设备。
POWER_SUPPLY_PROP_CAPACITY_LEVEL,容量,包括“Unknown”,“Critical”,“Low”,“Normal”,“High”,“Full”,由枚举型变量(POWER_SUPPLY_CAPACITY_LEVEL_*)定义。一般用于 battery 类型的 power supply 设备。
power supply 设备类型由 enum power_supply_type 枚举定义。
POWER_SUPPLY_TYPE_UNKOWN,未知。
POWER_SUPPLY_TYPE_BATTERY,电池,嵌入式设备、手持式智能设备常用的供电形式。
POWER_SUPPLY_TYPE_UPS,Uninterruptible Power System/Uninterruptible Power Supply,不间断式供电设备,通过将交流电和蓄电池连接,正常情况下由交流电供电,同时向蓄电池充电。当交流电断电时,由蓄电池紧急供电。一般用于服务器等设备。
POWER_SUPPLY_TYPE_MAINS,主供电设备,如笔记本电脑的适配器,其特点是可以单独供电,当其断电时,再由辅助供电设备供电(如 battery)。
POWER_SUPPLY_TYPE_USB/POWER_SUPPLY_TYPE_USB_DCP/POWER_SUPPLY_TYPE_USB_CDP/POWER_SUPPLY_TYPE_USB_ACA/POWER_SUPPLY_TYPE_USB_PD/POWER_SUPPLY_TYPE_USB_PD_DRP,USB 类型的供电,不同点在于充电电流的限制,由 USB Battery Charge Spec 规定。
POWER_SUPPLY_TYPE_USB_TYPE_C USB Type C 接口供电。
POWER_SUPPLY_TYPE_APPLE_BRICK_ID 苹果供电。
power supply USB 类型由 power_supply_usb_type 枚举定义。
标准下行端口(SDP)
这种端口的D+和D-线上具有15kΩ下拉电阻。限流值为:挂起时2.5mA,连接时为100mA,连接并配置为较高功率时为500mA。它其实就是一种普通的USB模式,当USB处于这种模式时,既可以为外部设备(手机充电、充电宝)充电,也可以起到数据连接的作用(U盘、手机上传/下载)。
专用充电端口(DCP)
这种端口不支持任何数据传输,但能够提供1.5A以上的电流。端口的D+和D-线之间短路。这种类型的端口支持较高充电能力的墙上充电器和车载充电器,无需枚举。它其实就是简单的充电器,当USB处于这种模式时只能进行充电而不能进行数据连接。
充电下行端口(CDP)
这种端口既支持大电流充电,也支持完全兼容USB 2.0的数据传输。端口具有D+和D-通信所必需的15kΩ下拉电阻,也具有充电器检测阶段切换的内部电路。内部电路允许便携设备将CDP与其它类型端口区分开来。它其实就是带有快充功能(1.5A)的USB接口,当USB处于这种模式时既可以进行快充,也可以起到数据连接的作用。
USB-PD(Power Delivery)

基于USB Type-C的一种电源供电标准,最大供电功率可达100瓦(W);随着USB Type-C的普及,越来越多的设备(手机、平板、显示器、工作站、充电器等)使用USB-PD快速充电方案。
USB PD 1.0
PD1.0把传输功率从之前的7.5W提高到了最大100W,输出电压最高达到20V,最大电流达到5A。当一个用电设备连接到主机的USB口,初始功率为10W (5V / 2A),在最终的功率规格选定之后,传输功率相应的转换到18W,36W,60W或者100W。
USB PD 2.0
随着Type-C接口规范的发布,USB-IF将USB PD升级到了2.0版本。BUS电压根据需求在5V,9V,15V和20V之间切换。
Type-C是一种接口规范,默认最大支持5V/3A。Type-C接口带有专用的通信线,即CC(channel configure)线。CC线可以传输USB PD协议,同时支持DRP (dual role port) type C接口,可以在电源和负载之间进行角色转换,进而支持功率双向传输。
USB PD 3.0
是目前USB PD的最新版本。在PD2.0的基础上,PD3.0增加了PPS(programmable power supply)功能,BUS电压能够以20mV/step进行调整,电流限值能够以50mA/step进行调整。电压电流细分调整可以优化电力传输策略,让power变得更加智能化。

include/linux/power_supply.h
讯享网enum {
POWER_SUPPLY_STATUS_UNKNOWN = 0, POWER_SUPPLY_STATUS_CHARGING, POWER_SUPPLY_STATUS_DISCHARGING, POWER_SUPPLY_STATUS_NOT_CHARGING, POWER_SUPPLY_STATUS_FULL, }; enum {
POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0, POWER_SUPPLY_CHARGE_TYPE_NONE, POWER_SUPPLY_CHARGE_TYPE_TRICKLE, POWER_SUPPLY_CHARGE_TYPE_FAST, }; enum {
POWER_SUPPLY_HEALTH_UNKNOWN = 0, POWER_SUPPLY_HEALTH_GOOD, POWER_SUPPLY_HEALTH_OVERHEAT, POWER_SUPPLY_HEALTH_DEAD, POWER_SUPPLY_HEALTH_OVERVOLTAGE, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, POWER_SUPPLY_HEALTH_COLD, POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE, POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE, }; enum {
POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0, POWER_SUPPLY_TECHNOLOGY_NiMH, POWER_SUPPLY_TECHNOLOGY_LION, POWER_SUPPLY_TECHNOLOGY_LIPO, POWER_SUPPLY_TECHNOLOGY_LiFe, POWER_SUPPLY_TECHNOLOGY_NiCd, POWER_SUPPLY_TECHNOLOGY_LiMn, }; enum {
POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0, POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL, POWER_SUPPLY_CAPACITY_LEVEL_LOW, POWER_SUPPLY_CAPACITY_LEVEL_NORMAL, POWER_SUPPLY_CAPACITY_LEVEL_HIGH, POWER_SUPPLY_CAPACITY_LEVEL_FULL, }; enum {
POWER_SUPPLY_SCOPE_UNKNOWN = 0, POWER_SUPPLY_SCOPE_SYSTEM, POWER_SUPPLY_SCOPE_DEVICE, }; enum power_supply_property {
/* Properties of type `int' */ POWER_SUPPLY_PROP_STATUS = 0, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_AUTHENTIC, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_VOLTAGE_BOOT, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CURRENT_BOOT, POWER_SUPPLY_PROP_POWER_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_EMPTY, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_AVG, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL, POWER_SUPPLY_PROP_ENERGY_EMPTY, POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_AVG, POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP_MAX, POWER_SUPPLY_PROP_TEMP_MIN, POWER_SUPPLY_PROP_TEMP_ALERT_MIN, POWER_SUPPLY_PROP_TEMP_ALERT_MAX, POWER_SUPPLY_PROP_TEMP_AMBIENT, POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN, POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_USB_TYPE, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_PRECHARGE_CURRENT, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_CALIBRATE, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, }; enum power_supply_type {
POWER_SUPPLY_TYPE_UNKNOWN = 0, POWER_SUPPLY_TYPE_BATTERY, POWER_SUPPLY_TYPE_UPS, POWER_SUPPLY_TYPE_MAINS, 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 */ POWER_SUPPLY_TYPE_USB_TYPE_C, /* Type C Port */ POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery Port */ POWER_SUPPLY_TYPE_USB_PD_DRP, /* PD Dual Role Port */ POWER_SUPPLY_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ }; enum power_supply_usb_type {
POWER_SUPPLY_USB_TYPE_UNKNOWN = 0, POWER_SUPPLY_USB_TYPE_SDP, /* Standard Downstream Port */ POWER_SUPPLY_USB_TYPE_DCP, /* Dedicated Charging Port */ POWER_SUPPLY_USB_TYPE_CDP, /* Charging Downstream Port */ POWER_SUPPLY_USB_TYPE_ACA, /* Accessory Charger Adapters */ POWER_SUPPLY_USB_TYPE_C, /* Type C Port */ POWER_SUPPLY_USB_TYPE_PD, /* Power Delivery Port */ POWER_SUPPLY_USB_TYPE_PD_DRP, /* PD Dual Role Port */ POWER_SUPPLY_USB_TYPE_PD_PPS, /* PD Programmable Power Supply */ POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ };
三、Power Supply 核心实现
power supply framework 首要任务,是向 power supply 设备 driver 提供统一的驱动编写接口,主要包括:
power supply 设备的 register/unregister API
其中 power_supply_register 和 power_supply_register_no_ws 的区别是 power_supply_register 注册的设备,具备 wakeup 系统的能力,而 power_supply_register_no_ws 不具备。power_supply_register 和 devm_power_supply_register 的区别是 power_supply_register 需要对返回的 power_supply 指针使用 power_supply_unregister 来释放资源,而 devm_power_supply_register 返回的 power_supply 指针将在驱动程序 detach 时自动注销。
include/linux/power_supply.h
extern struct power_supply *__must_check power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg); extern struct power_supply *__must_check power_supply_register_no_ws(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg); extern struct power_supply *__must_check devm_power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg); extern struct power_supply *__must_check devm_power_supply_register_no_ws(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg); extern void power_supply_unregister(struct power_supply *psy);
power_supply_register、power_supply_register_no_ws、devm_power_supply_register 和 devm_power_supply_register_no_ws,内部都调用了 __power_supply_register,下面分析它的关键工作流程:
- 遍历 desc 指向的 power_supply_desc 结构中的属性字段,如果存在 USB 供电属性,那么就去查看是否存在 USB 类型,不存在的话返回 ERR_PTR(-EINVAL)。
- 调用 kzalloc 给 power_supply 结构分配内存,并调用 device_initialize 初始化 device 结构,接着给 dev 指向的 device 结构字段赋值。
- 调用 dev_set_drvdata 给 device 设置私有数据,指向刚刚分配的 power_supply 结构。
- 形参 cfg 不为空的情况下,将 cfg 携带的数据赋给 power_supply 结构中的相应字段。
- 调用 dev_set_name 设置 device name。
- 调用 INIT_WORK 和 INIT_DELAYED_WORK 宏初始化工作队列,并添加任务,INIT_DELAYED_WORK 添加的任务会延时执行。
- 调用 power_supply_check_supplies 检查给本 power supply 设备供电的 power supply 设备,这个函数可能到设备树去检索 supplies。
- 调用 spin_lock_init 初始化 power_supply 结构中的自旋锁。
- 调用 device_add 添加这个 device。
- 调用 device_init_wakeup 赋予设备是否支持唤醒系统,和是否使用唤醒系统的特性。
- 调用 psy_register_thermal 给 power supply 设备注册 thermal,和温控相关。
- 调用 psy_register_cooler 给 power supply 设备注册 cooler,和温控相关。
- 调用 power_supply_create_triggers 创建触发器。
- power_supply 结构中的 use_cnt 原子的加 1(在任何 uevents 之后更新 use_cnt,最明显的来自 device_add() )。
- power_supply 结构中的 initialized 字段赋值为 true,表明 power_supply 结构已经初始化完成。
- 调用 queue_delayed_work 启动 delayed work 执行,延迟 POWER_SUPPLY_DEFERRED_REGISTER_TIME 后执行。
- 没有异常的情况下,现在返回指向 power_supply 结构的指针。
power_supply_unregister 主要工作流程:
- 调用 atomic_dec_return 将 power_supply 结构中的 use_cnt 原子地减一。
- power_supply 结构中的 removing 字段赋值为 true,表征正在移除 power supply 设备。
- 调用 cancel_work_sync 和 cancel_delayed_work_sync 取消两个工作队列里的任务。
- 调用 sysfs_remove_link 删除 kobj 目录下名为 powers 的软链接文件。
- 调用 power_supply_remove_triggers 移除触发器。
- 调用 psy_unregister_cooler 和 psy_unregister_thermal 取消注册的 cooler 和 thermal。
- 调用 device_init_wakeup 取消 power supply 设备唤醒系统的能力。
- 调用 device_unregister 移除设备。
drivers/power/supply/power_supply_core.c
讯享网static struct power_supply *__must_check __power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg, bool ws) {
struct device *dev; struct power_supply *psy; int i, rc; if (!parent) pr_warn("%s: Expected proper parent device for '%s'\n", __func__, desc->name); if (!desc || !desc->name || !desc->properties || !desc->num_properties) return ERR_PTR(-EINVAL); for (i = 0; i < desc->num_properties; ++i) {
if ((desc->properties[i] == POWER_SUPPLY_PROP_USB_TYPE) && (!desc->usb_types || !desc->num_usb_types)) return ERR_PTR(-EINVAL); } psy = kzalloc(sizeof(*psy), GFP_KERNEL); if (!psy) return ERR_PTR(-ENOMEM); dev = &psy->dev; 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->desc = desc; if (cfg) {
psy->drv_data = cfg->drv_data; psy->of_node = cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node; psy->supplied_to = cfg->supplied_to; psy->num_supplicants = cfg->num_supplicants; } rc = dev_set_name(dev, "%s", desc->name); if (rc) goto dev_set_name_failed; INIT_WORK(&psy->changed_work, power_supply_changed_work); INIT_DELAYED_WORK(&psy->deferred_register_work, power_supply_deferred_register_work); rc = power_supply_check_supplies(psy); if (rc) {
dev_info(dev, "Not all required supplies found, defer probe\n"); goto check_supplies_failed; } spin_lock_init(&psy->changed_lock); rc = device_add(dev); if (rc) goto device_add_failed; rc = device_init_wakeup(dev, ws); 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; /* * Update use_cnt after any uevents (most notably from device_add()). * We are here still during driver's probe but * the power_supply_uevent() calls back driver's get_property * method so: * 1. Driver did not assigned the returned struct power_supply, * 2. Driver could not finish initialization (anything in its probe * after calling power_supply_register()). */ atomic_inc(&psy->use_cnt); psy->initialized = true; queue_delayed_work(system_power_efficient_wq, &psy->deferred_register_work, POWER_SUPPLY_DEFERRED_REGISTER_TIME); return psy; create_triggers_failed: psy_unregister_cooler(psy); register_cooler_failed: psy_unregister_thermal(psy); register_thermal_failed: device_del(dev); wakeup_init_failed: device_add_failed: check_supplies_failed: dev_set_name_failed: put_device(dev); return ERR_PTR(rc); } struct power_supply *__must_check power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg) {
return __power_supply_register(parent, desc, cfg, true); } EXPORT_SYMBOL_GPL(power_supply_register); struct power_supply *__must_check power_supply_register_no_ws(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg) {
return __power_supply_register(parent, desc, cfg, false); } EXPORT_SYMBOL_GPL(power_supply_register_no_ws); static void devm_power_supply_release(struct device *dev, void *res) {
struct power_supply **psy = res; power_supply_unregister(*psy); } struct power_supply *__must_check devm_power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg) {
struct power_supply **ptr, *psy; ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); psy = __power_supply_register(parent, desc, cfg, true); if (IS_ERR(psy)) {
devres_free(ptr); } else {
*ptr = psy; devres_add(parent, ptr); } return psy; } EXPORT_SYMBOL_GPL(devm_power_supply_register); struct power_supply *__must_check devm_power_supply_register_no_ws(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg) {
struct power_supply **ptr, *psy; ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); psy = __power_supply_register(parent, desc, cfg, false); if (IS_ERR(psy)) {
devres_free(ptr); } else {
*ptr = psy; devres_add(parent, ptr); } return psy; } EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws); void power_supply_unregister(struct power_supply *psy) {
WARN_ON(atomic_dec_return(&psy->use_cnt)); psy->removing = true; cancel_work_sync(&psy->changed_work); cancel_delayed_work_sync(&psy->deferred_register_work); sysfs_remove_link(&psy->dev.kobj, "powers"); power_supply_remove_triggers(psy); psy_unregister_cooler(psy); psy_unregister_thermal(psy); device_init_wakeup(&psy->dev, false); device_unregister(&psy->dev); } EXPORT_SYMBOL_GPL(power_supply_unregister);
power supply 设备的 状态改变时通知 power supply core API
当 power supply 设备 driver 检测到该设备某些属性值改变时,需要调用这个接口,通知 power supply core。
include/linux/power_supply.h
extern void power_supply_changed(struct power_supply *psy);
- 调用 spin_lock_irqsave 获取 power_supply 结构中 changed_lock 锁之前关闭本地中断。
- 修改 power_supply 结构中 changed 字段为 true。
- 调用 pm_stay_awake 通知 PM 核心正在处理一个唤醒事件。
- 调用 spin_unlock_irqrestore 解锁,并恢复本地中断到没调用 spin_lock_irqsave 之前的状态。
- 调用 schedule_work 将 changed_work 任务提交到工作队列。
drivers/power/supply/power_supply_core.c
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); } EXPORT_SYMBOL_GPL(power_supply_changed);
由于注册 power supply 设备的时候工作任务关联的函数是 power_supply_changed_work,当工作线程执行到这个任务时会调用 power_supply_changed_work 函数。
power_supply_changed_work 主要工作流程:
- 调用 container_of 宏获取 power_supply 结构。
- 调用 spin_lock_irqsave 获取 power_supply 结构中 changed_lock 锁之前关闭本地中断。
- 当 power_supply 结构中的 changed 字段为 true,表明有工作需要进一步处理,先将 changed 字段设置为 false,接着调用 spin_unlock_irqrestore 解锁,并恢复本地中断到没调用 spin_lock_irqsave 之前的状态。然后调用 class_for_each_device 从设备列表的开头开始迭代 power_supply_class,并为每个设备调用 __power_supply_changed_work,传递 power_supply 结构。接下来调用 power_supply_update_leds 更新 LED 指示灯的状态。调用 atomic_notifier_call_chain 依次调用通知程序链(power_supply_notifier)中的每个函数,函数在原子上下文中运行,所以它们不能阻塞。继续调用 kobject_uevent 触发 CHANGE 类型的 uevent。最后继续调用 spin_lock_irqsave 获取 power_supply 结构中 changed_lock 锁之前关闭本地中断。
在这里检查’changed’,以避免 power_supply_changed 和这个例程之间的竞争导致的问题。在最坏的情况下,power_supply_changed 可以在上锁之前再次调用。在这个例程的第一次调用中,我们将把’changed’标记为 false,并且在下一次调用中它也将保持为 false。
- 运行到这里,如果 power_supply 结构中的 changed 字段为 false,就调动 pm_relax 通知 PM 核心一个唤醒事件的处理已经结束。最后调用 spin_unlock_irqrestore 解锁,并恢复本地中断到没调用 spin_lock_irqsave 之前的状态。
保持 wakeup_source 直到所有事件处理完毕。power_supply_changed 可能会再次调用并将’changed’设置为 true。
__power_supply_changed_work 主要工作流程:
- 调用目标 power supply 设备 dev_get_drvdata 获取 power_supply struct。
- 调用 __power_supply_is_supplied_by 查找是否有其它 power supply 设备给目标设备供电。如果 __power_supply_is_supplied_by 返回 true,表明的确有。检查目标设备 external_power_changed 字段是否为空,调用目标设备的 external_power_changed 通知给它供电的 power supply 设备。
__power_supply_is_supplied_by 主要工作流程:
- 检查目标 power supply 设备的 supplied_from 和可能给目标供电的 power supply 设备 supplied_to 列表,如果同时为空直接返回 false。
- 查找目标 power supply 设备的 supplied_from 列表,如果存在名字和可能给目标供电的 power supply 设备的名字一样,说明这个设备的确给目标设备供电,直接返回 true。
- 如果目标 power supply 设备的 supplied_from 列表为空,就去查找可能给目标供电的 power supply 设备 supplied_to 列表,如果找到目标 power supply 设备的名字,说明的确给它供电了,也返回 true。
drivers/power/supply/power_supply_core.c
static bool __power_supply_is_supplied_by(struct power_supply *supplier, struct power_supply *supply) {
int i; if (!supply->supplied_from && !supplier->supplied_to) return false; /* Support both supplied_to and supplied_from modes */ if (supply->supplied_from) {
if (!supplier->desc->name) return false; for (i = 0; i < supply->num_supplies; i++) if (!strcmp(supplier->desc->name, supply->supplied_from[i])) return true; } else {
if (!supply->desc->name) return false; for (i = 0; i < supplier->num_supplicants; i++) if (!strcmp(supplier->supplied_to[i], supply->desc->name)) return true; } return false; } static int __power_supply_changed_work(struct device *dev, void *data) {
struct power_supply *psy = data; struct power_supply *pst = dev_get_drvdata(dev); if (__power_supply_is_supplied_by(psy, pst)) {
if (pst->desc->external_power_changed) pst->desc->external_power_changed(pst); } return 0; } 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); /* * Check 'changed' here to avoid issues due to race between * power_supply_changed() and this routine. In worst case * power_supply_changed() can be called again just before we take above * lock. During the first call of this routine we will mark 'changed' as * false and it will stay false for the next call as well. */ if (likely(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); atomic_notifier_call_chain(&power_supply_notifier, PSY_EVENT_PROP_CHANGED, psy); kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE); spin_lock_irqsave(&psy->changed_lock, flags); } /* * Hold the wakeup_source until all events are processed. * power_supply_changed() might have called again and have set 'changed' * to true. */ if (likely(!psy->changed)) pm_relax(&psy->dev); spin_unlock_irqrestore(&psy->changed_lock, flags); }
power supply 设备的 获取/释放 API
include/linux/power_supply.h
extern struct power_supply *power_supply_get_by_name(const char *name); extern void power_supply_put(struct power_supply *psy); #ifdef CONFIG_OF extern struct power_supply *power_supply_get_by_phandle(struct device_node *np, const char *property); extern struct power_supply *devm_power_supply_get_by_phandle( struct device *dev, const char *property); #else /* !CONFIG_OF */ ... #endif /* CONFIG_OF */
根据名称搜索匹配的 power_supply 结构,如果找到,它会增加内部 power supply 设备的参考计数。用户在使用后应该使用 power_supply_put。
power_supply_get_by_name 主要工作流程:
- 调用 class_find_device 用于定位设备迭代器中和 name 匹配的设备,name 匹配是使用 power_supply_match_device_by_name 实现的,此函数内部调用了 strcmp 比较 name 字符串。
- 从 device 结构体中调用 dev_get_drvdata 获取其私有数据 power_supply 结构体。
- power_supply 结构体 use_cnt 字段原子地加 1。
power_supply_put 解除通过 power_supply_get_by_name 获取的引用。在注销 power supply 设备前应该解除引用。其内部首先原子地将 use_cnt 字段减 1,再去调用 put_device 减少对 device 的引用计数。
power_supply_get_by_phandle 通过持有 phandle 属性的设备节点和包含 powe supply 设备名称的属性获取相应的 power_supply 结构。
drivers/power/supply/power_supply_core.c
static int power_supply_match_device_by_name(struct device *dev, const void *data) {
const char *name = data; struct power_supply *psy = dev_get_drvdata(dev); return strcmp(psy->desc->name, name) == 0; } struct power_supply *power_supply_get_by_name(const char *name) {
struct power_supply *psy = NULL; struct device *dev = class_find_device(power_supply_class, NULL, name, power_supply_match_device_by_name); if (dev) {
psy = dev_get_drvdata(dev); atomic_inc(&psy->use_cnt); } return psy; } EXPORT_SYMBOL_GPL(power_supply_get_by_name); void power_supply_put(struct power_supply *psy) {
might_sleep(); atomic_dec(&psy->use_cnt); put_device(&psy->dev); } EXPORT_SYMBOL_GPL(power_supply_put); #ifdef CONFIG_OF static int power_supply_match_device_node(struct device *dev, const void *data) {
return dev->parent && dev->parent->of_node == data; } struct power_supply *power_supply_get_by_phandle(struct device_node *np, const char *property) {
struct device_node *power_supply_np; struct power_supply *psy = NULL; struct device *dev; power_supply_np = of_parse_phandle(np, property, 0); if (!power_supply_np) return ERR_PTR(-ENODEV); dev = class_find_device(power_supply_class, NULL, power_supply_np, power_supply_match_device_node); of_node_put(power_supply_np); if (dev) {
psy = dev_get_drvdata(dev); atomic_inc(&psy->use_cnt); } return psy; } EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); static void devm_power_supply_put(struct device *dev, void *res) {
struct power_supply **psy = res; power_supply_put(*psy); } struct power_supply *devm_power_supply_get_by_phandle(struct device *dev, const char *property) {
struct power_supply **ptr, *psy; if (!dev->of_node) return ERR_PTR(-ENODEV); ptr = devres_alloc(devm_power_supply_put, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); psy = power_supply_get_by_phandle(dev->of_node, property); if (IS_ERR_OR_NULL(psy)) {
devres_free(ptr); } else {
*ptr = psy; devres_add(dev, ptr); } return psy; } EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle); #endif /* CONFIG_OF */
power supply 设备的 向其它 driver 提供的用于接收 power supply 设备状态改变 notifier 的 API
include/linux/power_supply.h
extern struct atomic_notifier_head power_supply_notifier; extern int power_supply_reg_notifier(struct notifier_block *nb); extern void power_supply_unreg_notifier(struct notifier_block *nb);
power_supply_reg_notifier 调用 atomic_notifier_chain_register 原子地添加 notifier_block 到通知链。power_supply_unreg_notifier 刚好相反。
drivers/power/supply/power_supply_core.c
int power_supply_reg_notifier(struct notifier_block *nb) {
return atomic_notifier_chain_register(&power_supply_notifier, nb); } EXPORT_SYMBOL_GPL(power_supply_reg_notifier); void power_supply_unreg_notifier(struct notifier_block *nb) {
atomic_notifier_chain_unregister(&power_supply_notifier, nb); }
power supply 设备的 属性 API
这些属性相关的 API 只是简单的做了下检查后,将工作委托给 power_supply desc 指向的 power_supply_desc 结构体中的具体实现函数处理。
include/linux/power_supply.h
extern int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); extern int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val); extern int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp);
drivers/power/supply/power_supply_core.c
int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) {
if (atomic_read(&psy->use_cnt) <= 0) {
if (!psy->initialized) return -EAGAIN; return -ENODEV; } return psy->desc->get_property(psy, psp, val); } EXPORT_SYMBOL_GPL(power_supply_get_property); int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) {
if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property) return -ENODEV; return psy->desc->set_property(psy, psp, val); } EXPORT_SYMBOL_GPL(power_supply_set_property); int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) {
if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->property_is_writeable) return -ENODEV; return psy->desc->property_is_writeable(psy, psp); } EXPORT_SYMBOL_GPL(power_supply_property_is_writeable);
power supply 设备的 其他 API
power_supply_get_battery_info 获取电池信息(power_supply_battery_info 代表)。
power_supply_set_input_current_limit_from_supplier 设置来自 power supply 设备的输入电流限制。
power_supply_set_battery_charged 调用指定 power supply 设备的 set_charged 回调。
power_supply_is_system_supplied 查询系统是否有有效的或者处于 online 状态的 power supply 设备,如果没有,可能为桌面系统。
power_supply_am_i_supplied 查询自己是否由其它 power supply 设备供电。
power_supply_external_power_changed 外部供电设备“变化”,调用 external_power_changed 回调函数处理。
power_supply_powers 在指定设备(通常是该 power supply 设备)的 Sysfs 目录(/sys/devices/xxx/)下,创建指定 power supply 设备的符号链接(/sys/devices/xxx/powers)。
power_supply_get_drvdata 获取 power_supply 结构的 driver 私有数据。
include/linux/power_supply.h
extern int power_supply_get_battery_info(struct power_supply *psy, struct power_supply_battery_info *info); extern int power_supply_am_i_supplied(struct power_supply *psy); extern int power_supply_am_i_supplied(struct power_supply *psy); extern int power_supply_set_input_current_limit_from_supplier( struct power_supply *psy); extern int power_supply_set_battery_charged(struct power_supply *psy); extern int power_supply_is_system_supplied(void); extern void power_supply_external_power_changed(struct power_supply *psy); extern int power_supply_powers(struct power_supply *psy, struct device *dev); extern void *power_supply_get_drvdata(struct power_supply *psy);
- 判断 power_supply 结构中的 of_node 是否为空,如果为空就打印 warn log:当前仅支持设备树。
- 调用 of_parse_phandle 通过当前节点的 phandle 属性(monitored-battery),获得一个 device node 节点(battery_np)。
- 从 battery_np 指向的 device node 中读出各种属性对应的值,填充到 power_supply_battery_info 结构体对应字段。
drivers/power/supply/power_supply_core.c
int power_supply_get_battery_info(struct power_supply *psy, struct power_supply_battery_info *info) {
struct device_node *battery_np; const char *value; int err; info->energy_full_design_uwh = -EINVAL; info->charge_full_design_uah = -EINVAL; info->voltage_min_design_uv = -EINVAL; info->precharge_current_ua = -EINVAL; info->charge_term_current_ua = -EINVAL; info->constant_charge_current_max_ua = -EINVAL; info->constant_charge_voltage_max_uv = -EINVAL; if (!psy->of_node) {
dev_warn(&psy->dev, "%s currently only supports devicetree\n", __func__); return -ENXIO; } battery_np = of_parse_phandle(psy->of_node, "monitored-battery", 0); if (!battery_np) return -ENODEV; err = of_property_read_string(battery_np, "compatible", &value); if (err) return err; if (strcmp("simple-battery", value)) return -ENODEV; /* The property and field names below must correspond to elements * in enum power_supply_property. For reasoning, see * Documentation/power/power_supply_class.txt. */ of_property_read_u32(battery_np, "energy-full-design-microwatt-hours", &info->energy_full_design_uwh); of_property_read_u32(battery_np, "charge-full-design-microamp-hours", &info->charge_full_design_uah); of_property_read_u32(battery_np, "voltage-min-design-microvolt", &info->voltage_min_design_uv); of_property_read_u32(battery_np, "precharge-current-microamp", &info->precharge_current_ua); of_property_read_u32(battery_np, "charge-term-current-microamp", &info->charge_term_current_ua); of_property_read_u32(battery_np, "constant_charge_current_max_microamp", &info->constant_charge_current_max_ua); of_property_read_u32(battery_np, "constant_charge_voltage_max_microvolt", &info->constant_charge_voltage_max_uv); return 0; } EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
power_supply_set_input_current_limit_from_supplier 主要工作流程:
- 调用 class_for_each_device 遍历 power_supply_class,此功能不打算用于具有多个供电源的情况,我们只是选择第一个供电源报告非0最大电流。
- __power_supply_get_supplier_max_current 函数中,获取到给该 power supply 设备供电的 power supply 设备,读取最大电流值并返回,这会导致 class_for_each_device 迭代退出循环。
- 调用 set_property 给该 power supply 设备设置最大电流值限制。
drivers/power/supply/power_supply_core.c
static int __power_supply_get_supplier_max_current(struct device *dev, void *data) {
union power_supply_propval ret = {
0,}; struct power_supply *epsy = dev_get_drvdata(dev); struct power_supply *psy = data; if (__power_supply_is_supplied_by(epsy, psy)) if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_CURRENT_MAX, &ret)) return ret.intval; return 0; } int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy) {
union power_supply_propval val = {
0,}; int curr; if (!psy->desc->set_property) return -EINVAL; /* * This function is not intended for use with a supply with multiple * suppliers, we simply pick the first supply to report a non 0 * max-current. */ curr = class_for_each_device(power_supply_class, NULL, psy, __power_supply_get_supplier_max_current); if (curr <= 0) return (curr == 0) ? -ENODEV : curr; val.intval = curr; return psy->desc->set_property(psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); } EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
当 power supply 设备读取用户数大于等于 0,并且其类型为电池时,而且它的 set_charged 不为空,就调用 set_charged 指向的实际实现函数。
drivers/power/supply/power_supply_core.c
int power_supply_set_battery_charged(struct power_supply *psy) {
if (atomic_read(&psy->use_cnt) >= 0 && psy->desc->type == POWER_SUPPLY_TYPE_BATTERY && psy->desc->set_charged) {
psy->desc->set_charged(psy); return 0; } return -EINVAL; } EXPORT_SYMBOL_GPL(power_supply_set_battery_charged);
调用 class_for_each_device 遍历 power_supply_class,查找系统是否有有效的或者处于 online 状态的 power supply 设备(非电池供电类型),如果遍历完后 count 还为 0,说明根本没有发现 power supply 设备,那么很可能我们正在桌面系统上运行,因此假设我们使用的是主电源供电。
__power_supply_is_system_supplied 主要获取 POWER_SUPPLY_PROP_ONLINE 属性判断 power supply 设备是否在线。
drivers/power/supply/power_supply_core.c
static int __power_supply_is_system_supplied(struct device *dev, void *data) {
union power_supply_propval ret = {
0,}; struct power_supply *psy = dev_get_drvdata(dev); unsigned int *count = data; (*count)++; if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) return ret.intval; return 0; } int power_supply_is_system_supplied(void) {
int error; unsigned int count = 0; error = class_for_each_device(power_supply_class, NULL, &count, __power_supply_is_system_supplied); /* * If no power class device was found at all, most probably we are * running on a desktop system, so assume we are on mains power. */ if (count == 0) return 1; return error; } EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
调用 class_for_each_device 遍历 power_supply_class,当查到存在设备给其供电(设备的 POWER_SUPPLY_PROP_ONLINE 属性如果不为 0), 就 break 出来。
drivers/power/supply/power_supply_core.c
struct psy_am_i_supplied_data {
struct power_supply *psy; unsigned int count; }; static int __power_supply_am_i_supplied(struct device *dev, void *_data) {
union power_supply_propval ret = {
0,}; struct power_supply *epsy = dev_get_drvdata(dev); struct psy_am_i_supplied_data *data = _data; if (__power_supply_is_supplied_by(epsy, data->psy)) {
data->count++; if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) return ret.intval; } return 0; } int power_supply_am_i_supplied(struct power_supply *psy) {
struct psy_am_i_supplied_data data = {
psy, 0 }; int error; error = class_for_each_device(power_supply_class, NULL, &data, __power_supply_am_i_supplied); dev_dbg(&psy->dev, "%s count %u err %d\n", __func__, data.count, error); if (data.count == 0) return -ENODEV; return error; } EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
power supply core 调用 power_supply_external_power_changed 函数非常简单,判断用户数大于 0 的情况下,直接调用 external_power_changed 函数指针指向的实际函数实现。
drivers/power/supply/power_supply_core.c
void power_supply_external_power_changed(struct power_supply *psy) {
if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->external_power_changed) return; psy->desc->external_power_changed(psy); } EXPORT_SYMBOL_GPL(power_supply_external_power_changed);
调用 sysfs_create_link 在 Sysfs 创建一个该 power supply 设备(我们要在其目录中创建链接)指向的 dev 设备符号链接 powers。
drivers/power/supply/power_supply_core.c
int power_supply_powers(struct power_supply *psy, struct device *dev) {
return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers"); } EXPORT_SYMBOL_GPL(power_supply_powers);
调用 sysfs_create_link 在 Sysfs 创建一个该 power supply 设备(我们要在其目录中创建链接)指向的 dev 设备符号链接 powers。
drivers/power/supply/power_supply_core.c
int power_supply_powers(struct power_supply *psy, struct device *dev) {
return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers"); } EXPORT_SYMBOL_GPL(power_supply_powers);
power_supply_get_drvdata 实现非常简单仅仅返回了 power_supply 结构的 drv_data 字段。
drivers/power/supply/power_supply_core.c
void *power_supply_get_drvdata(struct power_supply *psy) {
return psy->drv_data; } EXPORT_SYMBOL_GPL(power_supply_get_drvdata);
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/23739.html