深入Linux Power Supply Class:以fan54015驱动为例,图解PSY设备注册与属性上报机制
2026/5/23 7:53:02 网站建设 项目流程

Linux Power Supply Class深度解析:从驱动注册到用户空间事件的全链路实现

在嵌入式系统开发中,电源管理始终是影响设备稳定性和用户体验的关键因素。Linux内核的Power Supply子系统(简称PSY)作为连接硬件驱动与用户空间的桥梁,其设计精妙程度往往决定了电源管理的灵活性和可靠性。本文将以fan54015充电芯片驱动为例,深入剖析PSY设备从内核注册到用户空间通知的完整生命周期。

1. Power Supply子系统的架构设计

Linux的Power Supply子系统采用典型的分层设计,完美体现了Unix"一切皆文件"的哲学。其核心架构可分为三个层次:

  • 硬件抽象层:通过struct power_supplystruct power_supply_desc等数据结构封装各类电源设备
  • 核心服务层:处理属性管理、事件通知等公共逻辑
  • 用户接口层:通过sysfs和uevent机制向用户空间暴露统一接口

这种设计使得不同厂商的电源芯片只需实现必要的硬件操作接口,就能无缝接入Linux电源管理体系。以fan54015驱动为例,其关键数据结构如下:

static const struct power_supply_desc fan54015_charger_desc = { .name = "fan54015_charger", .type = POWER_SUPPLY_TYPE_USB, .properties = fan54015_usb_props, .num_properties = ARRAY_SIZE(fan54015_usb_props), .get_property = fan54015_charger_usb_get_property, .set_property = fan54015_charger_usb_set_property, .property_is_writeable = fan54015_charger_property_is_writeable, .usb_types = fan54015_charger_usb_types, .num_usb_types = ARRAY_SIZE(fan54015_charger_usb_types), };

该描述符定义了设备名称、类型、支持的属性列表以及对应的回调函数,是驱动与子系统交互的契约。值得注意的是,properties数组定义了该PSY设备对外暴露的接口能力,例如:

static enum power_supply_property fan54015_usb_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_USB_TYPE, };

2. PSY设备的注册流程剖析

PSY设备的注册通常发生在驱动的probe阶段,这个过程涉及多个关键步骤:

  1. 资源分配与初始化:获取设备树配置,初始化硬件寄存器
  2. 工作队列准备:初始化changed_work用于异步事件处理
  3. 描述符填充:如前一节所示的power_supply_desc配置
  4. 正式注册:调用power_supply_register()完成注册

fan54015驱动的注册代码片段如下:

info->psy = power_supply_register(dev, &fan54015_charger_desc, &cfg); if (IS_ERR(info->psy)) { dev_err(dev, "Failed to register power supply\n"); return PTR_ERR(info->psy); }

注册过程中,内核会执行以下关键操作:

  • 在/sys/class/power_supply/下创建对应目录
  • 根据描述符中的属性列表生成sysfs文件节点
  • 初始化内核对象关联关系
  • 设置uevent回调机制

特别值得注意的是power_supply_config结构体,它允许驱动传递一些注册时的特定配置:

struct power_supply_config { struct device_node *of_node; /* Driver private data */ void *drv_data; char **supplied_to; size_t num_supplicants; char **supplied_from; size_t num_supplies; bool attach_extcon; };

其中supplied_tosupplied_from字段用于建立PSY设备间的供耗关系,这在多电源系统中尤为重要。

3. 属性操作的实现机制

PSY子系统的核心价值在于它提供了统一的属性访问接口。驱动需要实现两个关键回调函数:

  • get_property:当用户空间读取sysfs属性时触发
  • set_property:当用户空间写入sysfs属性时触发

fan54015的实现示例如下:

static int fan54015_charger_usb_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct fan54015_charger_info *info = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = fan54015_charger_get_status(info); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: val->intval = info->cur.cur; break; /* 其他属性处理 */ default: return -EINVAL; } return 0; }

属性值的传递使用union power_supply_propval联合体,可以适应不同类型的属性值:

union power_supply_propval { int intval; const char *strval; };

对于可写属性,驱动还需要实现property_is_writeable回调,告知内核哪些属性允许修改:

static int fan54015_charger_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: return 1; default: return 0; } }

4. 状态变更与用户空间通知

当硬件状态发生变化时,驱动需要及时通知用户空间。这个过程通过power_supply_changed()函数触发:

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); }

该函数的核心工作流程是:

  1. 设置changed标志位
  2. 防止系统进入深度睡眠
  3. 调度工作队列处理异步通知

在工作队列中,内核会完成以下操作:

  • 更新sysfs节点值
  • 发送uevent事件
  • 调用notifier链通知其他内核模块

对于Android系统,这个uevent会被healthd服务捕获,进而触发电量更新广播。开发者可以通过以下命令观察PSY设备的uevent:

# 监控power_supply类的uevent udevadm monitor --property --subsystem-match=power_supply

5. 多PSY设备的协同工作

在实际系统中,往往存在多个PSY设备协同工作的情况。例如典型的移动设备可能包含:

PSY设备类型功能描述典型驱动文件
Battery电池状态管理charger-manager.c
USBUSB充电管理fan54015-charger.c
AC交流充电管理sc2721-charger.c
Fuel Gauge电量计量sc27xx_fuel_gauge.c

这些设备通过supplied_to/supplied_from建立关联关系。当某个PSY状态变化时,内核会自动通知依赖它的其他PSY设备。这种设计使得电源管理策略可以灵活配置,而不需要硬编码在驱动中。

6. 调试技巧与性能优化

开发PSY驱动时,以下几个调试技巧非常实用:

  1. sysfs接口检查

    # 查看所有PSY设备 ls /sys/class/power_supply/ # 查看特定设备属性 cat /sys/class/power_supply/fan54015_charger/status
  2. 内核动态调试

    # 启用PSY子系统的动态调试 echo 'file power_supply_* +p' > /sys/kernel/debug/dynamic_debug/control
  3. 性能优化要点

    • 减少power_supply_changed()的调用频率
    • 对高频变化的属性使用power_supply_changed_work()延迟通知
    • 合理设置psy_desc中的no_thermal标志避免不必要的温控检查
  4. 电源状态跟踪

    # 跟踪PSY相关函数调用 perf probe --add 'power_supply_changed' perf probe --add 'power_supply_get_property'

7. 设备���配置与硬件抽象

良好的设备树配置可以使驱动更加灵活。fan54015的典型设备树配置如下:

fan54015_chg: charger@6a { compatible = "fairchild,fan54015_chg"; reg = <0x6a>; phys = <&hsphy>; monitored-battery = <&bat>; extcon = <&extcon_gpio>; vddvbus:otg-vbus { regulator-name = "vddvbus"; }; };

关键配置项包括:

  • reg:I2C设备地址
  • monitored-battery:关联的电池节点
  • extcon:USB连接器状态检测

对应的电池节点需要详细定义电池参数:

bat: battery { compatible = "simple-battery"; charge-full-design-microamp-hours = <3900000>; charge-term-current-microamp = <200000>; constant_charge_voltage_max_microvolt = <4400000>; voltage-min-design-microvolt = <3561000>; /* 温度补偿表等更多参数 */ };

这些配置信息会被驱动通过power_supply_get_battery_info()接口读取,用于实现精确的充电控制。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询