Linux 4.19.111 供电(power supply )子系统

Linux 4.19.111 供电(power supply )子系统Linux 内核中为了方便对 battery 的管理 专门提供了 power supply framework battery 管理分开为两个部分 一个是电池监控 fuelgauge 另一个是充放电管理 charger fuelgauge 驱动主要负责向上层 android

大家好,我是讯享网,很高兴认识大家。

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 子系统为驱动提供了统一的框架。功能包括:

  1. 抽象 power supply 设备的共性,向用户空间提供统一的 API;
  2. 为 power supply 设备驱动的编写提供简单、统一的方式。

power supply framework 主要由3部分组成:

  1. power supply core,通用电源监控类,用于抽象核心数据结构、实现公共逻辑。位于 drivers/power/power_supply_core.c 中。
  2. power supply sysfs,通用电源监控类的 Sysfs 接口,实现 Sysfs 以及 uevent 功能。位于 drivers/power/power_supply_sysfs.c 中。
  3. 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,下面分析它的关键工作流程:

  1. 遍历 desc 指向的 power_supply_desc 结构中的属性字段,如果存在 USB 供电属性,那么就去查看是否存在 USB 类型,不存在的话返回 ERR_PTR(-EINVAL)。
  2. 调用 kzalloc 给 power_supply 结构分配内存,并调用 device_initialize 初始化 device 结构,接着给 dev 指向的 device 结构字段赋值。
  3. 调用 dev_set_drvdata 给 device 设置私有数据,指向刚刚分配的 power_supply 结构。
  4. 形参 cfg 不为空的情况下,将 cfg 携带的数据赋给 power_supply 结构中的相应字段。
  5. 调用 dev_set_name 设置 device name。
  6. 调用 INIT_WORK 和 INIT_DELAYED_WORK 宏初始化工作队列,并添加任务,INIT_DELAYED_WORK 添加的任务会延时执行。
  7. 调用 power_supply_check_supplies 检查给本 power supply 设备供电的 power supply 设备,这个函数可能到设备树去检索 supplies。
  8. 调用 spin_lock_init 初始化 power_supply 结构中的自旋锁。
  9. 调用 device_add 添加这个 device。
  10. 调用 device_init_wakeup 赋予设备是否支持唤醒系统,和是否使用唤醒系统的特性。
  11. 调用 psy_register_thermal 给 power supply 设备注册 thermal,和温控相关。
  12. 调用 psy_register_cooler 给 power supply 设备注册 cooler,和温控相关。
  13. 调用 power_supply_create_triggers 创建触发器。
  14. power_supply 结构中的 use_cnt 原子的加 1(在任何 uevents 之后更新 use_cnt,最明显的来自 device_add() )。
  15. power_supply 结构中的 initialized 字段赋值为 true,表明 power_supply 结构已经初始化完成。
  16. 调用 queue_delayed_work 启动 delayed work 执行,延迟 POWER_SUPPLY_DEFERRED_REGISTER_TIME 后执行。
  17. 没有异常的情况下,现在返回指向 power_supply 结构的指针。

power_supply_unregister 主要工作流程:

  1. 调用 atomic_dec_return 将 power_supply 结构中的 use_cnt 原子地减一。
  2. power_supply 结构中的 removing 字段赋值为 true,表征正在移除 power supply 设备。
  3. 调用 cancel_work_sync 和 cancel_delayed_work_sync 取消两个工作队列里的任务。
  4. 调用 sysfs_remove_link 删除 kobj 目录下名为 powers 的软链接文件。
  5. 调用 power_supply_remove_triggers 移除触发器。
  6. 调用 psy_unregister_cooler 和 psy_unregister_thermal 取消注册的 cooler 和 thermal。
  7. 调用 device_init_wakeup 取消 power supply 设备唤醒系统的能力。
  8. 调用 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); 
  1. 调用 spin_lock_irqsave 获取 power_supply 结构中 changed_lock 锁之前关闭本地中断。
  2. 修改 power_supply 结构中 changed 字段为 true。
  3. 调用 pm_stay_awake 通知 PM 核心正在处理一个唤醒事件。
  4. 调用 spin_unlock_irqrestore 解锁,并恢复本地中断到没调用 spin_lock_irqsave 之前的状态。
  5. 调用 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 主要工作流程:

  1. 调用 container_of 宏获取 power_supply 结构。
  2. 调用 spin_lock_irqsave 获取 power_supply 结构中 changed_lock 锁之前关闭本地中断。
  3. 当 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。

  1. 运行到这里,如果 power_supply 结构中的 changed 字段为 false,就调动 pm_relax 通知 PM 核心一个唤醒事件的处理已经结束。最后调用 spin_unlock_irqrestore 解锁,并恢复本地中断到没调用 spin_lock_irqsave 之前的状态。

保持 wakeup_source 直到所有事件处理完毕。power_supply_changed 可能会再次调用并将’changed’设置为 true。

__power_supply_changed_work 主要工作流程:

  1. 调用目标 power supply 设备 dev_get_drvdata 获取 power_supply struct。
  2. 调用 __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 主要工作流程:

  1. 检查目标 power supply 设备的 supplied_from 和可能给目标供电的 power supply 设备 supplied_to 列表,如果同时为空直接返回 false。
  2. 查找目标 power supply 设备的 supplied_from 列表,如果存在名字和可能给目标供电的 power supply 设备的名字一样,说明这个设备的确给目标设备供电,直接返回 true。
  3. 如果目标 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 主要工作流程:

  1. 调用 class_find_device 用于定位设备迭代器中和 name 匹配的设备,name 匹配是使用 power_supply_match_device_by_name 实现的,此函数内部调用了 strcmp 比较 name 字符串。
  2. 从 device 结构体中调用 dev_get_drvdata 获取其私有数据 power_supply 结构体。
  3. 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); 
  1. 判断 power_supply 结构中的 of_node 是否为空,如果为空就打印 warn log:当前仅支持设备树。
  2. 调用 of_parse_phandle 通过当前节点的 phandle 属性(monitored-battery),获得一个 device node 节点(battery_np)。
  3. 从 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 主要工作流程:

  1. 调用 class_for_each_device 遍历 power_supply_class,此功能不打算用于具有多个供电源的情况,我们只是选择第一个供电源报告非0最大电流。
  2. __power_supply_get_supplier_max_current 函数中,获取到给该 power supply 设备供电的 power supply 设备,读取最大电流值并返回,这会导致 class_for_each_device 迭代退出循环。
  3. 调用 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); 
小讯
上一篇 2025-01-07 15:22
下一篇 2025-03-02 20:11

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/23739.html