Linux Power supply子系统分析

  • Post author:
  • Post category:linux







1.




概述





对于便携式设备,如手机或者


pad


来说,


battery


都是必不可少的一个组成部分。


kernel


中为了方便对


battery


的管理,专门提供了


power supply framework






battery


管理我觉得可以分开为两个部分,一个是



电池监控(




fuelgauge












另一个是充放电管理(




charger








,所以我们在内核中也是把它分成了两个驱动来管理。




电池监控(




fuelgauge








驱动主要是负责向上层


android


系统提供当前电池的



电量



以及



健康状态



信息等等,另外除了这个以外,它



也向




charger




驱动提供电池的相关信息








充放电管理(




charger








驱动主要负责



电源线的插拔检测



,以及



充放电的过程



管理。



对于




battery




管理,硬件上有电量计




IC




和充放电




IC












2.Power Supply




设计思路




先来回答一个问题:


kernel


中设备驱动的目的,是管理设备,并提供给用户空间程序使用,那么对


PSY


设备而言,


kernel


要管理什么?用户空间程序要使用什么?


其实


PSY


设备是一个特例,它的目的很单纯,就为系统供电。如果只考虑这个目的,就不需要任何驱动了,但情况会稍微复杂,因为:


1.

PSY




设备可能是电池,这引申出来电量检测






充电管理等多个议题。此时,



PSY driver




需要管理的事情包括:




检测电池类型









检测电池电量









检测充电状态




等等。而用户空间则需要将检测到的




结果显示给用户







2.


系统中可能有多个


PSY


设备,这些设备可能还有级联关系,如有些平板电脑中,

可能同时存在



DC-charger





USB-charger





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.Power Supply




软件架构



power supply framework





kernel/drivers/power/


下。内核抽象出来


power supply


子系统为驱动提供了统一的框架。功能包括:


1.


抽象


PSY


设备的共性,向用户空间提供统一的


API


2.


为底层


PSY


驱动的编写,提供简单、统一的方式。同事封装并实现公共逻辑。



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/




目录下。



https://img-blog.csdn.net/20180507172954608


接着来看一下核心数据结构


drivers/power/power_supply_core.c


中。


1.struct power_supply


struct power_supply





power supply class


的核心数据结构,用于抽象


PSY


设备,定义如下


(kernel/msm-4.19/include/linux/power_supply.h)





  1. struct




    power_supply {




  2. const




    char




    *name;




    //









    PSY




    的名称



  3. enum




    power_supply_type type;




    //









    PSY




    的类型,枚举类型,一般包括




    :battery









    USB-charger









    DC-charger








  4. enum




    power_supply_property *properties;




    //









    PSY




    具有的属性列表,枚举型。



  5. #ifdef CONFIG_CHARGING_NODEADD



Int charge_node_add    //




需要添加节点的话要在这里添加节点属性



#endif



  1. size_t num_properties;




    //




    属性的个数



  2. char




    **supplied_to;




    //




    一个字符串数组,保存了由该




    PSY




    供电的




    PSY




    列表,以此可将




    PAY




    组成互相级联的




    PSY




    链表。这些被供电的




    PSY




    ,称作




    supplicant




    (客户端,乞求者)



  3. size_t num_supplicants;




    //supplicant




    的个数。



  4. char




    **supplied_from;




    //




    一个字符串数组,保存了该




    PSY




    供电的




    PSY




    列表,也称作




    supply




    (提供者),从另外一个方向组织




    PSY




    之间的级联关系。



  5. size_t num_supplies;




    //supply




    的个数



  6. #




    ifdef




    CONFIG_OF



  7. struct




    device_node *of_node;




    //




    用于保存




    of_node




    指针。



  8. #




    endif



  9. int




    (*get_property)(




    struct




    power_supply *psy,




    //PSY driver




    需要实现的回调函数,用于获取属性值。



  10. enum




    power_supply_property psp,



  11. union




    power_supply_propval *val);



  12. int




    (*set_property)(




    struct




    power_supply *psy,




    //PSY driver




    需要实现的回调函数,用于设置属性值。



  13. enum




    power_supply_property psp,



  14. const




    union




    power_supply_propval *val);



  15. int




    (*property_is_writeable)(




    struct




    power_supply *psy,




    //




    返回指定的属性值是否可写(用于




    sysfs




    );



  16. enum




    power_supply_property psp);



  17. void




    (*external_power_changed)(




    struct




    power_supply *psy);




    //




    当一个




    PSY




    设备存在




    supply PSY




    ,且该




    supply PSY




    的属性发生改变(如




    online offline




    )时,




    power supply core




    会调用该回调函数,通知




    PSY driver




    以便让它做出相应处理。



  18. void




    (*set_charged)(




    struct




    power_supply *psy);




    //




    该回调函数的应用场景有点奇怪,外部模块通知




    PAY driver




    ,该




    PSY




    设备的状态改变了,自己改变了自己不知道,要外部通知,希望大家在实际工作中不要遇到,太纠结了。



  19. /* For APM emulation, think legacy userspace. */



  20. int




    use_for_apm;



  21. /* private */



  22. struct




    device *dev;



  23. struct




    work_struct changed_work;




    //




    用户处理状态改变的




    work queue




    ,主要思路是当该




    PSY




    的状态发生改变,启动一个




    workqueue




    ,查询并通知所有的




    supplicants








  24. spinlock_t changed_lock;



  25. bool




    changed;



  26. #




    ifdef




    CONFIG_THERMAL



  27. struct




    thermal_zone_device *tzd;



  28. struct




    thermal_cooling_device *tcd;



  29. #




    endif



  30. #




    ifdef




    CONFIG_LEDS_TRIGGERS



  31. struct




    led_trigger *charging_full_trig;



  32. char




    *charging_full_trig_name;



  33. struct




    led_trigger *charging_trig;



  34. char




    *charging_trig_name;



  35. struct




    led_trigger *full_trig;



  36. char




    *full_trig_name;



  37. struct




    led_trigger *online_trig;




    //




    如果配置了




    CONFIC_LED_TRIGGERS




    ,则调用的




    linux led class




    的接口,注册相应的




    LED




    设备,用于




    PSY




    状态指示。



  38. char




    *online_trig_name;



  39. struct




    led_trigger *charging_blink_full_solid_trig;



  40. char




    *charging_blink_full_solid_trig_name;



  41. #




    endif



  42. };


2.PSY


的类型


PSY


类型由


enum power_supply_type(power_supply.h)


定义:



  1. enum




    power_supply_type {




  2. POWER_SUPPLY_TYPE_UNKNOWN =




    0




    ,




    //




    未知



  3. POWER_SUPPLY_TYPE_BATTERY,




    //




    电池,手机平板上面最常用的



  4. POWER_SUPPLY_TYPE_UPS,




    //




    不间断供电的,一般用户服务器



  5. POWER_SUPPLY_TYPE_MAINS,




    //




    主供电设备,如笔记本电脑适配器,特点是可以单独供电,当其断开时,再由辅助设备供电。



  6. POWER_SUPPLY_TYPE_USB,




    /* Standard Downstream Port */



  7. POWER_SUPPLY_TYPE_USB_DCP,




    /* Dedicated Charging Port */



  8. POWER_SUPPLY_TYPE_USB_CDP,




    /* Charging Downstream Port */



  9. POWER_SUPPLY_TYPE_USB_ACA,




    /* Accessory Charger Adapters */



  10. };


3.PSY


属性


power supply class


将所有可能


PSY


属性


(power_supply.h)


,以枚举型变量形式抽象出来,


PSY driver


可以根据设备的实际情况,从中选取一些。



  1. enum power_supply_property {




  2. /* Properties of type `int’ */



  3. POWER_SUPPLY_PROP_STATUS = 0, //









    PSY









    status




    ,主要是充电状态,包括:




    unknown,charging,discharging,not charging full,



  4. POWER_SUPPLY_PROP_CHARGE_TYPE,//




    充电类型



  5. POWER_SUPPLY_PROP_HEALTH, //




    健康状况,包括:




    good dead  over voltage








  6. POWER_SUPPLY_PROP_PRESENT, //




    电量百分比



  7. POWER_SUPPLY_PROP_ONLINE,  //




    是否在线



  8. POWER_SUPPLY_PROP_AUTHENTIC,



  9. POWER_SUPPLY_PROP_TECHNOLOGY, //




    采用的技术



  10. POWER_SUPPLY_PROP_CYCLE_COUNT,



  11. POWER_SUPPLY_PROP_VOLTAGE_MAX,



  12. POWER_SUPPLY_PROP_VOLTAGE_MIN,



  13. POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,



  14. POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,



  15. POWER_SUPPLY_PROP_VOLTAGE_NOW,



  16. POWER_SUPPLY_PROP_VOLTAGE_AVG,



  17. POWER_SUPPLY_PROP_VOLTAGE_OCV,



  18. POWER_SUPPLY_PROP_CURRENT_MAX,



  19. POWER_SUPPLY_PROP_CURRENT_NOW,



  20. POWER_SUPPLY_PROP_CURRENT_AVG,



  21. POWER_SUPPLY_PROP_POWER_NOW,



  22. POWER_SUPPLY_PROP_POWER_AVG,



  23. POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,



  24. POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,



  25. POWER_SUPPLY_PROP_CHARGE_FULL,



  26. POWER_SUPPLY_PROP_CHARGE_EMPTY,



  27. POWER_SUPPLY_PROP_CHARGE_NOW,



  28. POWER_SUPPLY_PROP_CHARGE_AVG,



  29. POWER_SUPPLY_PROP_CHARGE_COUNTER,



  30. POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,



  31. POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,



  32. POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,



  33. POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,



  34. POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,



  35. POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,



  36. POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,



  37. POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,



  38. POWER_SUPPLY_PROP_ENERGY_FULL,



  39. POWER_SUPPLY_PROP_ENERGY_EMPTY,



  40. POWER_SUPPLY_PROP_ENERGY_NOW,



  41. POWER_SUPPLY_PROP_ENERGY_AVG,



  42. POWER_SUPPLY_PROP_CAPACITY, /* in percents! */



  43. POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */



  44. POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */



  45. POWER_SUPPLY_PROP_CAPACITY_LEVEL, //




    容量



  46. POWER_SUPPLY_PROP_TEMP,



  47. POWER_SUPPLY_PROP_TEMP_ALERT_MIN,



  48. POWER_SUPPLY_PROP_TEMP_ALERT_MAX,



  49. POWER_SUPPLY_PROP_TEMP_AMBIENT,



  50. POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,



  51. POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,



  52. POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,



  53. POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,



  54. POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,



  55. POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,



  56. POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */



  57. POWER_SUPPLY_PROP_SCOPE,



  58. /* Local extensions */



  59. POWER_SUPPLY_PROP_USB_HC,



  60. POWER_SUPPLY_PROP_USB_OTG,



  61. POWER_SUPPLY_PROP_CHARGE_ENABLED,



  62. /* Local extensions of type int64_t */



  63. POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,



  64. /* Properties of type `const char *’ */



  65. POWER_SUPPLY_PROP_MODEL_NAME,



  66. POWER_SUPPLY_PROP_MANUFACTURER,



  67. POWER_SUPPLY_PROP_SERIAL_NUMBER,



  68. };





4.




向具体的PSY driver提供的API




power supply class




首要任务,是向




PSY driver




提供统一的驱动编程接口



,主要包括:


1.PSY





register/unregister API





driver/power/power_supply_core.c





  1. extern int




    power_supply_register




    (struct device *parent,



  2. struct power_supply *psy);



  3. extern void




    power_supply_unregister




    (struct power_supply *psy);



  4. extern int




    power_supply_powers




    (struct power_supply *psy, struct device *dev);


2.


PSY


状态改变时通知


power supply core





API



extern void




power_supply_changed




(struct power_supply *psy);





PSY driver


检测到该设备某些



属性改变



时,需要



调用这个接口



(



power_supply_changed




(struct power_supply *psy)





)




,通知


power supply core





power supply core


会有如下动作


如果该


PSY


是其他


PSY


的供电源,调用这些


PSY





external_power_changed


回调函数,通知他们(这些


PSY


具体要做什么,由他们自身的逻辑决定);


发生


notifier


通知那些关心


PSY


设备状态的


drivers





以统一的格式向用户空间发送




uevent



。(这就是设备模型中


class


的魅力,对外接口由


class core


提供,可以节省


driver


的工作量,同时确保了接口的一致性)。


3.


其他杂项接口



  1. extern




    struct




    power_supply *power_supply_get_by_name(




    const




    char




    *name);





    //






    通过名字获取






    PSY






    指针




  2. extern




    int




    power_supply_am_i_supplied(




    struct




    power_supply *psy);





    //






    查询自己是否由其他






    PSY






    供电




  3. extern




    int




    power_supply_set_battery_charged(




    struct




    power_supply *psy);





    //






    调用指定的






    PSY













    set_changed






    回调。




  4. extern




    int




    power_supply_is_system_supplied(




    void




    );





    //






    检查系统中是否有有效的或者处于






    online






    状态的






    PSY






    ,如果没有,可能为桌面系统、




  5. extern




    int




    power_supply_powers(




    struct




    power_supply *psy,




    struct




    device *dev);





    //






    在指定的设备的






    sysfs






    目录下创建指定的






    PSY






    符合连接。




5.


向用户空间提供的


API




power supply class




通过两种形式向用户空间提供接口。




1)uevent,以“名字


=value”


的形式,上报所有


property


的值,格式如下:



  1. POWER_SUPPLY_NAME=xxx

    /* power supply name */




  2. POWER_SUPPLY_xxx1=xxx

    /* property = value */




  3. POWER_SUPPLY_xxx2=xxx


uevent


一般会在


PSY


设备添加到


kernel


时,或者


PSY


属性发生改变时发生。


2


)sysfs,power supply class





power_supply_sysfs.c


中,定义了相当多的默认


attribute


,如果某个


PSY


设备具有某个属性,该属性对应的


attribute


就会体现在


sysfs


中(一般位于


“/sys/class/power_supply/xxx/”


中)




  1. /* Must be in the same order as POWER_SUPPLY_PROP_* */




  2. static




    struct




    device_attribute power_supply_attrs[] = {





  3. /* Properties of type `int’ */




  4. POWER_SUPPLY_ATTR(status),



  5. POWER_SUPPLY_ATTR(charge_type),



  6. POWER_SUPPLY_ATTR(health),



  7. POWER_SUPPLY_ATTR(present),



  8. POWER_SUPPLY_ATTR(online),



  9. POWER_SUPPLY_ATTR(authentic),



  10. POWER_SUPPLY_ATTR(technology),



  11. POWER_SUPPLY_ATTR(cycle_count),



  12. POWER_SUPPLY_ATTR(voltage_max),



  13. POWER_SUPPLY_ATTR(voltage_min),



  14. POWER_SUPPLY_ATTR(voltage_max_design),



  15. POWER_SUPPLY_ATTR(voltage_min_design),



  16. POWER_SUPPLY_ATTR(voltage_now),



  17. POWER_SUPPLY_ATTR(voltage_avg),



  18. POWER_SUPPLY_ATTR(voltage_ocv),



  19. POWER_SUPPLY_ATTR(current_max),



  20. POWER_SUPPLY_ATTR(current_now),



  21. POWER_SUPPLY_ATTR(current_avg),



  22. POWER_SUPPLY_ATTR(power_now),



  23. POWER_SUPPLY_ATTR(power_avg),



  24. POWER_SUPPLY_ATTR(charge_full_design),



  25. POWER_SUPPLY_ATTR(charge_empty_design),



  26. POWER_SUPPLY_ATTR(charge_full),



  27. POWER_SUPPLY_ATTR(charge_empty),



  28. POWER_SUPPLY_ATTR(charge_now),



  29. POWER_SUPPLY_ATTR(charge_avg),



  30. POWER_SUPPLY_ATTR(charge_counter),



  31. POWER_SUPPLY_ATTR(constant_charge_current),



  32. POWER_SUPPLY_ATTR(constant_charge_current_max),



  33. POWER_SUPPLY_ATTR(constant_charge_voltage),



  34. POWER_SUPPLY_ATTR(constant_charge_voltage_max),



  35. POWER_SUPPLY_ATTR(charge_control_limit),



  36. POWER_SUPPLY_ATTR(charge_control_limit_max),



  37. POWER_SUPPLY_ATTR(energy_full_design),



  38. POWER_SUPPLY_ATTR(energy_empty_design),



  39. POWER_SUPPLY_ATTR(energy_full),



  40. POWER_SUPPLY_ATTR(energy_empty),



  41. POWER_SUPPLY_ATTR(energy_now),



  42. POWER_SUPPLY_ATTR(energy_avg),



  43. POWER_SUPPLY_ATTR(capacity),



  44. POWER_SUPPLY_ATTR(capacity_alert_min),



  45. POWER_SUPPLY_ATTR(capacity_alert_max),



  46. POWER_SUPPLY_ATTR(capacity_level),



  47. POWER_SUPPLY_ATTR(temp),



  48. POWER_SUPPLY_ATTR(temp_alert_min),



  49. POWER_SUPPLY_ATTR(temp_alert_max),



  50. POWER_SUPPLY_ATTR(temp_ambient),



  51. POWER_SUPPLY_ATTR(temp_ambient_alert_min),



  52. POWER_SUPPLY_ATTR(temp_ambient_alert_max),



  53. POWER_SUPPLY_ATTR(time_to_empty_now),



  54. POWER_SUPPLY_ATTR(time_to_empty_avg),



  55. POWER_SUPPLY_ATTR(time_to_full_now),



  56. POWER_SUPPLY_ATTR(time_to_full_avg),



  57. POWER_SUPPLY_ATTR(type),



  58. POWER_SUPPLY_ATTR(scope),




  59. /* Local extensions */




  60. POWER_SUPPLY_ATTR(usb_hc),



  61. POWER_SUPPLY_ATTR(usb_otg),



  62. POWER_SUPPLY_ATTR(charge_enabled),




  63. /* Local extensions of type int64_t */




  64. POWER_SUPPLY_ATTR(charge_counter_ext),




  65. /* Properties of type `const char *’ */




  66. POWER_SUPPLY_ATTR(model_name),



  67. POWER_SUPPLY_ATTR(manufacturer),



  68. POWER_SUPPLY_ATTR(serial_number),



  69. };





6.




怎样基于power supply class编写PSY driver



最后从


PSY driver


的角度,说明一下怎么基于


power supply class


编写驱动:


(1






根据硬件spec




,确定




PSY




设备具备哪些特性,并把他们和




enum power_supply_property




对应。





2






根据实际情况,实现这些




properties









get/set




接口。





3






定义一个




struct power_supply




变量,并初始化必要字段后,调用




power_supply_register




或者




power_supply_register_no_ws




,将其注册到




kernel




中。





4






根据实际情况,启动设备属性变化的监控逻辑,例如中断,轮询等,并在发生改变时,调用




power_supply_changed




,通知




power suopply core








power supply




子系统的引入



以市面上一款常见的的平板方案来看一看,进入



平板的



sys/class/power_supply/


目录下

https://img-blog.csdn.net/20180525154253933?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTI4MzAxNDg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70


可以看到这里有

三个




PSY




设备,分别对应




USB




充电器




DC




充电器



,和电池。


进入


battery


目录下,发现下面有各种各样的属性,另外两个


atc260x-usb





atc260x-wall


目录下分别也是这样。

https://img-blog.csdn.net/2018052515440585?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTI4MzAxNDg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70


那么在内核中肯定有三个驱动对应这个三个


PSY设备。



PSY设备驱动分析



我们先以



battery




驱动为例



来分析。



  1. static int __init atc260x_gauge_init(void)



  2. {




  3. struct device_node *node =



  4. of_find_compatible_node(NULL, NULL, “actions,atc2603c-battery”);//




    获取设备树中对应的属性节点



  5. if (!node) {




  6. GAUGE_INFO(“%s fail to find atc2603c-battery node\n”, __func__);



  7. return 0;



  8. }



  9. GAUGE_INFO(“atc2603c_battery:version(%s), time stamp(%s)\n”,



  10. ATC2603C_BATTERY_DRV_VERSION, ATC2603C_BATTERY_DRV_TIMESTAMP);



  11. return platform_driver_register(&atc260x_gauge_driver);   //









    PSY




    设备驱动注册为




    platform




    设备驱动



  12. }


看看


atc260x_gauge_driver





probe


函数。



  1. static  int atc260x_gauge_probe(struct platform_device *pdev)



  2. {




  3. struct atc260x_gauge_info *info; //



  4. int ret;



  5. info = kzalloc(sizeof(struct atc260x_gauge_info), GFP_KERNEL); //




    分配一个




    atc260x_gauge_info



  6. if (info == NULL)



  7. return -ENOMEM;



  8. info->atc260x = atc260x;



  9. info->node = pdev->dev.of_node;   //




    一些初始化工作



  10. global_gauge_info_ptr = info;



  11. first_store_gauge_info = 1;



  12. mutex_init(&info->lock);



  13. platform_set_drvdata(pdev, info);



  14. /*init battery power supply*/     //




    这里就是




    power supply




    相关的了



  15. info->battery.name = “battery”;  //









    name









    battery




    ”就是对应我们在




    /sys/class/power_supply/




    目录下看到的




    battery



  16. info->battery.use_for_apm = 1;



  17. info->battery.type = POWER_SUPPLY_TYPE_BATTERY; //




    设备




    type



  18. info->battery.properties = atc260x_gauge_props; //




    设置属性列表



  19. info->battery.num_properties = ARRAY_SIZE(atc260x_gauge_props); //




    设置属性列表的大小



  20. info->battery.get_property = atc260x_gauge_get_props;  //




    设置获取属性值的回调函数。



  21. ret =

    power_supply_register(&pdev->dev, &info->battery); //






    将这个






    power_supply






    设备注册进内核中。




  22. 。。。




    //




    其他电量计相关的省略



  23. return ret;



}


从上面可以看出


编写


PSY driver


相当的简单。就调用了一个


power_supply_register


函数。


后面再电量计的轮询函数中,如果检测到相关的属性发生了变化。就调用


power_supply_changed


函数,上报给


power supply core





  1. static void soc_post_process(struct atc260x_gauge_info *info)



  2. {




  3. int soc_last;



  4. if (info->soc_pre != info->soc_show) {




  5. info->soc_pre = info->soc_show;



  6. power_supply_changed(&info->battery); //




    如果




    soc




    发生了变化,则调用




    power_supply_changed




    上报变化信息。



  7. }



  8. info->bat_temp = get_battery_temperature();



  9. if (info->pre_temp!= info->bat_temp) {




  10. info->pre_temp = info->bat_temp;



  11. power_supply_changed(&info->battery); //




    如果温度发生了变化也进行上报



  12. }



  13. //




    省略其他不相关的。。。



  14. }


对于


USB





WALL





PSY


设备,其编程方法也是类似的,这里不再赘述。




power supply framework


的分析




先从


power_supply_register


分析起



  1. int power_supply_register(struct device *parent, struct power_supply *psy)



  2. {




  3. struct device *dev;



  4. int rc;



  5. dev = kzalloc(sizeof(*dev), GFP_KERNEL); //




    分配一个




    device




    结构



  6. if (!dev)



  7. return -ENOMEM;



  8. device_initialize(dev);



  9. dev->class = power_supply_class;  //




    指定




    power_supply_class



  10. dev->type = &power_supply_dev_type; //




    指定设备类型



  11. dev->parent = parent;               //




    指定此设备的父设备



  12. dev->release = power_supply_dev_release;



  13. dev_set_drvdata(dev, psy);



  14. psy->dev = dev;



  15. INIT_WORK(&psy->changed_work, power_supply_changed_work); //




    初始化一个




    power_supply_changed_work




    工做队列



  16. rc = power_supply_check_supplies(psy);



  17. if (rc) {




  18. dev_info(dev, “Not all required supplies found, defer probe\n”);



  19. goto check_supplies_failed;



  20. }



  21. rc = kobject_set_name(&dev->kobj, “%s”, psy->name);  //




    设置




    kobject









    name




    ,其实就是“




    battery








  22. if (rc)



  23. goto kobject_set_name_failed;



  24. rc = device_add(dev);  //




    将这个设备添加到设备链表中去



  25. if (rc)



  26. goto device_add_failed;



  27. spin_lock_init(&psy->changed_lock);



  28. rc = device_init_wakeup(dev, true);



  29. if (rc)



  30. goto wakeup_init_failed;



  31. rc = psy_register_thermal(psy);



  32. if (rc)



  33. goto register_thermal_failed;



  34. rc = psy_register_cooler(psy);



  35. if (rc)



  36. goto register_cooler_failed;



  37. rc = power_supply_create_triggers(psy);



  38. if (rc)



  39. goto create_triggers_failed;



  40. power_supply_changed(psy); //




    调用一次




    changed




    ,上报一次属性值



  41. goto success;



  42. }


接着看看



power_supply_changed



  1. void power_supply_changed(struct power_supply *psy)



  2. {




  3. unsigned long flags;



  4. dev_dbg(psy->dev, “%s\n”, __func__);



  5. spin_lock_irqsave(&psy->changed_lock, flags);



  6. psy->changed = true;



  7. pm_stay_awake(psy->dev);



  8. spin_unlock_irqrestore(&psy->changed_lock, flags);



  9. schedule_work(

    &psy->changed_work

    ); //









    psy->changed_work




    任务提交到工做队列,这个工做队列就是在




    power_supply_register




    中初始化的



  10. }



  11. EXPORT_SYMBOL_GPL(power_supply_changed);


既然如此,那我们就应该继续去看


psy->changed_work


的工作队列函数。


power_supply_changed_work

​​​​​​​



  1. static void power_supply_changed_work(struct work_struct *work)



  2. {




  3. unsigned long flags;



  4. struct power_supply *psy = container_of(work, struct power_supply, //




    获取到




    PSY



  5. changed_work);



  6. dev_dbg(psy->dev, “%s\n”, __func__);



  7. spin_lock_irqsave(&psy->changed_lock, flags);



  8. if (psy->changed) {                    //




    已经在




    power_supply_changed




    中被设置成




    true




    了。



  9. psy->changed = false;



  10. spin_unlock_irqrestore(&psy->changed_lock, flags);



  11. class_for_each_device(power_supply_class, NULL, psy,



  12. __power_supply_changed_work);



  13. power_supply_update_leds(psy);          //




    更改




    led




    指示状态




  14. kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); //






    发送






    uevent






    事件,通知应用层




  15. spin_lock_irqsave(&psy->changed_lock, flags);



  16. }



  17. if (!psy->changed)



  18. pm_relax(psy->dev);



  19. spin_unlock_irqrestore(&psy->changed_lock, flags);



  20. }


至此,


每当驱动中计算出


battery


的电量信息发生了变化,或者充电器的插拔状态发生了变化,驱动都会调用


power_supply_changed函数,随后在power_supply_changed中会启动工作队列,上报uevent事件,通知Android层去sysfs中读取电池相关的属性信息,展现给用户空间。


/drivers/power/supply/qcom/qpnp-smb5.c   charger driver


/drivers/power/supply/qcom/smb5-lib.c


依赖的


SMB library


/arch/arm64/boot/dts/qcom/pmi632.dtsi     dtsi

https://img-blog.csdnimg.cn/2019062811202677.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J1cnRfeXU=,size_16,color_FFFFFF,t_70




高通平台power_supply 框架下添加第三方充电IC的驱动方法




原文链接:


https://blog.csdn.net/stoic163/article/details/99680422


1.power_supply


电源框架介绍:


power supply framework





kernel/drivers/power/


下。内核抽象出来


power supply


子系统为驱动提供了统一的框架。


功能包括:


1.


抽象


PSY


设备的共性,向用户空间提供统一的


API;


2.


为底层


PSY驱动的编写,提供简单、统一的方式,同时封装并实现公共逻辑。


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/power_supply目录下。



在具体设备文件中在




/sys/class/power_supply,




具体如下:

https://img-blog.csdnimg.cn/20190816161208575.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70


power_supply


框架工作流程


Linux


的设备文件目录中可以在


sys/class/


下看到


power_supply


目录;


这个


power_supply


的类是通过


power_supply_core.c


文件中的


power_supply_class_init()


中的


class_create()


函数来进行


power_supply


类的创建


,如下:

https://img-blog.csdnimg.cn/20190816161208575.png



进入




power_supply




的目录下:

https://img-blog.csdnimg.cn/20190816161208573.png



我们可以看到出现了


battery





usb


两个目录,这两个目录就是充电


IC(battery)





USB(usb)


电源管理部分的注册的设备节点内容的集合。



进入


battery


目录下


:


https://img-blog.csdnimg.cn/20190816161208572.png



可以看到很多设备节点,这些节点就是通过


power_supply


的电源框架提供的方法进行注册并实现其内容的。



对于上述的节点内容,是怎么实现的,其实


power_supply


都提供了相应的节点名称,在文件


/kernel/include/linux/


power_supply.h


中提供了相关的



属性



枚举定义。如下:


https://img-blog.csdnimg.cn/20190816161208711.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70



这里提供了大部分的设备节点属性。作为驱动工程师,只需要选取芯片存在的并且自己需要的属性并实现其内容即可。






power_supply


的结构体中


(


也可能是在结构体之外


)







在这个结构体中,可以看到有





set_property











get_property





两个属性的函数指针




,这两个函数指针就是用来具体





实现相关属性





功能的。






power_supply_sysfy





文件系统




power_supply_



sysfs.c



文件中,具体进行的








设备节点









注册过程



,以及相关功能的实现:


https://img-blog.csdnimg.cn/20190816161208621.png



从上面可以看出,








power_supply_sysfs




文件中主要是实现




power_supply_attrs




数组中的成员的




show









store









先看


power_supply_attrs


数组





https://img-blog.csdnimg.cn/20190816161208732.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70



从上述内容看,


power_supply_attrs


数组其实就是实现之前


power_supply.h


文件中的枚举


power_supply_property








POWER_SUPPLY_ATTR(online)


中括号内容即是具体设备节点。


​​​​​​​






POWER_SUPPLY_ATTR


结构体中,有


show





store


两个指针。



show


的内容就是使用


cat  xxxx


节点的操作显示出来的内容的:


https://img-blog.csdnimg.cn/20190816161208729.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70




从上面可以看到有些节点显示的内容并不是数字,而是在




show




函数中被转换成了相关的字符串。如




:



https://img-blog.csdnimg.cn/20190816161208663.png









store




函数是




echo xx > xxx




节点的操作写入的内容。



https://img-blog.csdnimg.cn/20190816161208664.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70




在上述节点中,存在




uevent




这个节点,可以看看




uevent




节点的内容:



https://img-blog.csdnimg.cn/20190816161208667.png




这个节点由




power_supply_sysfs




文件中的




power_supply_uevent




函数实现的。



https://img-blog.csdnimg.cn/20190816161209721.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70



这个函数的作用就是将该设备节点下所有节点的内容组合成字符串发送到


uevent


中,在通过内核的


uevent


框架发送到用户空间。



当设备节点内容发生变化时,会调用


power_supply_changed


函数:


https://img-blog.csdnimg.cn/20190816161208690.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70




可以看到该函数是将




psy->changed_work




加入到工作队列中,具体的调用设备的




changed_work




的工作队列,看看具体做什么:



https://img-blog.csdnimg.cn/20190816161209685.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0b2ljMTYz,size_16,color_FFFFFF,t_70



可以看到,


对该类中查询每个设备的更新操作,再更新


led


灯的操作,还有就是发送新的事件


(uevent)


通知应用层。



至此


power_supply


就介绍结束了。



版权声明:本文为weixin_46376201原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。