天天看點

power_supply子系統作用結構體及apidriver流程添加一個新的節點

目錄

  • 作用
  • 結構體及api
    • 核心結構體
      • psy type
      • PSY property
    • 向具體的PSY driver提供的API
    • PSY狀态改變時通知power supply core的API
    • 其它雜項接口
    • 向其它driver提供的用于接收PSY狀态改變notifier的API
    • 向使用者空間傳值
  • driver流程
    • power_supply_sysfs.c
    • battery_probe
  • 添加一個新的節點

作用

psy driver的主要功能,就是向使用者空間程式彙整各類狀态資訊。

将這些狀态資訊,抽象為properties,由于狀态資訊的類型是有限的,properties的個數也是有限的。

psy driver隻需要負責:該psy裝置具有哪些“屬性”;這些“屬性”的value是什麼;當“屬性值”發生改變時,通知power supply class。

power supply class負責:将某個PSY裝置支援的屬性及其value,以sysfs的形式,提供給使用者空間;當屬性值改變時,以uevent的形式,廣播給使用者空間程式。

結構體及api

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

核心結構體

path:kernel-4.4/include/linux/power_supply.h

struct power_supply {
        const struct power_supply_desc *desc;

        char **supplied_to;//一個字元串數組,儲存了由該PSY供電的PSY清單,以此可将PSY組織成互相級聯的PSY鍊。這些“被供電”的PSY,稱作supplicant(用戶端、乞求者);
        size_t num_supplicants;//supplicant的個數;

        char **supplied_from;//一個字元串數組,儲存了向該PSY供電的PSY清單,也稱作supply(提供者)。從另一個方向,組織PSY之間的級聯關系;
        size_t num_supplies;//supply的個數;
        struct device_node *of_node;//用于儲存device、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;
//一個用于處理狀态改變的workqueue,主要思路是:當該PSY的狀态發生改變,啟動一個workqueue,查詢并通知所有的supplicants;
        atomic_t use_cnt;
#ifdef CONFIG_THERMAL
        struct thermal_zone_device *tzd;
        struct thermal_cooling_device *tcd;
//如果該PSY具有溫度等屬性,則需要借助linux generic thermal sysfs drivers(溫控子系統)的架構,注冊相應的thermal裝置
#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_desc 結構體

struct power_supply_desc {
        const char *name;    //裝置名稱    /sys/class/power_supply/name
        enum power_supply_type type; //電源類型
        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);//get property
        int (*set_property)(struct power_supply *psy,
                            enum power_supply_property psp,
                            const union power_supply_propval *val);
//PSY driver需要重點實作的兩個回調函數,用于擷取/設定屬性值;
        /*
         * 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);//傳回指定的屬性值是否可寫(用于sysfs);
        void (*external_power_changed)(struct power_supply *psy);//當一個PSY裝置存在supply PSY,且該supply PSY的屬性發生改變(如online、offline)時,power supply core會調用該回調函數,通知PSY driver,以便讓它做出相應的處理;
        void (*set_charged)(struct power_supply *psy);//外部子產品通知PSY driver,該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;
};

           

psy type

power_supply_type結構體

在mtk平台

enum power_supply_type {
        POWER_SUPPLY_TYPE_UNKNOWN = 0,
        POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
        POWER_SUPPLY_TYPE_WIRELESS, /* Wireless Charger */
};
           

标準的linux

enum power_supply_type {
        OWER_SUPPLY_TYPE_UNKNOWN = 0,
        POWER_SUPPLY_TYPE_BATTERY,//電池,嵌入式裝置、手持式智能裝置常用的供電形式;
        POWER_SUPPLY_TYPE_UPS,//Uninterruptible Power System/Uninterruptible Power Supply,不間斷式供電裝置,通過将交流電和蓄電池連接配接,正常情況下由交流電供電,同時向蓄電池充電。當交流電斷電時,由蓄電池緊急供電。一般用于伺服器等裝置;
        POWER_SUPPLY_TYPE_MAINS,//主供電裝置,如筆記本電腦的擴充卡,其特點是可以單獨供電,當其斷電時,再由輔助供電裝置供電(如battery);
        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 */
 };

           

PSY property

enum power_supply_property

向具體的PSY driver提供的API

extern int power_supply_register(struct device *parent,  struct power_supply *psy); //有wakeup system的能力
 extern int power_supply_register_no_ws(struct device *parent,struct power_supply *psy); //無wakeup system的能力
 
 extern void power_supply_unregister(struct power_supply *psy);

           

PSY狀态改變時通知power supply core的API

extern void power_supply_changed(struct power_supply *psy);
/**********************
當PSY driver檢測到該裝置某些屬性值改變時,需要調用這個接口,通知power supply core,power supply core會有如下動作:

如果該PSY是其它PSY的供電源,調用這些PSY的external_power_changed回調函數,通知它們(這些PSY具體要做些什麼,由它們的自身邏輯決定);

如果配置了CONFIG_LEDS_TRIGGERS,調用power_supply_update_leds,更新該PSY有關的led狀态;

發送notifier,通知那些關心PSY裝置狀态的drivers;

以統一的格式,向使用者空間發送uevent

***************************/
           

其它雜項接口

extern struct power_supply *power_supply_get_by_name(const char *name); //通過名字擷取PSY指針。
extern struct power_supply *power_supply_get_by_phandle(struct device_node *np,const char *property); //從DTS中,解析出對應dePSY指針。
extern int power_supply_am_i_supplied(struct power_supply *psy); //查詢自己是否由其它PSY供電。
extern int power_supply_set_battery_charged(struct power_supply *psy); //調用指定PSY的set_charged回調。
extern int power_supply_is_system_supplied(void); //查詢系統是否有有效的或者處于online狀态的PSY,如果沒有,可能為桌面系統。
extern int power_supply_powers(struct power_supply *psy, struct device *dev);//,在指定裝置(通常是該PSY裝置)的sysfs目錄(/sys/devices/xxx/)下,建立指定PSY的符号連結(/sys/devices/xxx/powers)。
           

向其它driver提供的用于接收PSY狀态改變notifier的API

extern int power_supply_reg_notifier(struct notifier_block *nb); //通過notifier注冊接口注冊notifier之後,系統任何PSY裝置的狀态發生改變,并調用了power_supply_changed接口,power supply core就是通知notifier的監聽者。
extern void power_supply_unreg_notifier(struct notifier_block *nb);
           

向使用者空間傳值

  1. uevnt
POWER_SUPPLY_NAME=xxx                     /* power supply name */ 
POWER_SUPPLY_xxx1=xxx                       /* property = value */ 
POWER_SUPPLY_xxx2=xxx 
…
           
  1. sysfs

    power supply class在power_supply_sysfs.c中,定義了相當多的預設attribute(見下面),如果某個PSY裝置具有某個屬性,該屬性對應的attribute就會展現在sysfs中

/* Must be in the same order as POWER_SUPPLY_PROP_* */
static struct device_attribute power_supply_attrs[] = {
        /* Properties of type `int' */
        POWER_SUPPLY_ATTR(status),
        POWER_SUPPLY_ATTR(charge_type),
        POWER_SUPPLY_ATTR(health),
        POWER_SUPPLY_ATTR(present),
        POWER_SUPPLY_ATTR(online),
        POWER_SUPPLY_ATTR(authentic),
        POWER_SUPPLY_ATTR(technology),
        POWER_SUPPLY_ATTR(cycle_count),
        POWER_SUPPLY_ATTR(voltage_max),
        POWER_SUPPLY_ATTR(voltage_min),
        POWER_SUPPLY_ATTR(voltage_max_design),
        POWER_SUPPLY_ATTR(voltage_min_design),
        POWER_SUPPLY_ATTR(voltage_now),
        POWER_SUPPLY_ATTR(voltage_avg),
        POWER_SUPPLY_ATTR(voltage_ocv),
        POWER_SUPPLY_ATTR(voltage_boot),
        POWER_SUPPLY_ATTR(current_max),
        POWER_SUPPLY_ATTR(current_now),
        POWER_SUPPLY_ATTR(current_avg),
        POWER_SUPPLY_ATTR(current_boot),
        POWER_SUPPLY_ATTR(power_now),

           

driver流程

建立一個名為power_supply的class,挂上一個uevent,之後填充device_type

power_supply_core.c
static struct device_type power_supply_dev_type;
static int __init power_supply_class_init(void)
{
 power_supply_class = class_create(THIS_MODULE, "power_supply");
 if (IS_ERR(power_supply_class))
  return PTR_ERR(power_supply_class);
 power_supply_class->dev_uevent = power_supply_uevent;
 power_supply_init_attrs(&power_supply_dev_type);
 return 0;
}
subsys_initcall(power_supply_class_init);
           

add_uevent_var中添加發送msg到uevent,如battery,後面的for循環中是将power_supply_attrs中的對應的屬性和值,以POWER_SUPPLY_XXXX=xxxx的方式添加到發送的資料中。這個回調函數就是做了這個操作,即将系統中的變化資料組織成特定的格式,通過uevent發送出去

power_supply_sysfs.c

int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
{
 struct power_supply *psy = dev_get_drvdata(dev);
 int ret = 0, j;
 char *prop_buf;
 char *attrname;
 dev_dbg(dev, "uevent\n");
 if (!psy || !psy->desc) {
  dev_dbg(dev, "No power supply yet\n");
  return ret;
 }
 dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name);
 ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
 if (ret)
  return ret;
 prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
 if (!prop_buf)
  return -ENOMEM;
 for (j = 0; j < psy->desc->num_properties; j++) {
  struct device_attribute *attr;
  char *line;
  attr = &power_supply_attrs[psy->desc->properties[j]];
  ret = power_supply_show_property(dev, attr, prop_buf);
  if (ret == -ENODEV || ret == -ENODATA) {
   /* When a battery is absent, we expect -ENODEV. Don't abort;
      send the uevent with at least the the PRESENT=0 property */
   ret = 0;
   continue;
  }
  if (ret < 0)
   goto out;
  line = strchr(prop_buf, '\n');
  if (line)
   *line = 0;
  attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
  if (!attrname) {
   ret = -ENOMEM;
   goto out;
  }
  dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
  ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
  kfree(attrname);
  if (ret)
   goto out;
 }
out:
 free_page((unsigned long)prop_buf);
 return ret;
}

           

填充sysfs

static struct attribute_group power_supply_attr_group = {
 .attrs = __power_supply_attrs,
 .is_visible = power_supply_attr_is_visible,
};
static const struct attribute_group *power_supply_attr_groups[] = {
 &power_supply_attr_group,
 NULL,
};
void power_supply_init_attrs(struct device_type *dev_type)
{
 int i;
 dev_type->groups = power_supply_attr_groups;
 for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
  __power_supply_attrs[i] = &power_supply_attrs[i].attr;
}
           

POWER_SUPPLY_ATTR

static struct device_attribute power_supply_attrs[] = {
 /* Properties of type `int' */
 POWER_SUPPLY_ATTR(status),
 POWER_SUPPLY_ATTR(charge_type),
 POWER_SUPPLY_ATTR(health),
 POWER_SUPPLY_ATTR(present),
 POWER_SUPPLY_ATTR(online),
 POWER_SUPPLY_ATTR(authentic),
 POWER_SUPPLY_ATTR(technology),
 POWER_SUPPLY_ATTR(cycle_count),
 POWER_SUPPLY_ATTR(voltage_max),
 POWER_SUPPLY_ATTR(voltage_min),
 POWER_SUPPLY_ATTR(voltage_max_design),
 POWER_SUPPLY_ATTR(voltage_min_design),
 POWER_SUPPLY_ATTR(voltage_now),
 POWER_SUPPLY_ATTR(voltage_avg),
......

           

battery_probe

在static int __init battery_probe(struct platform_device *dev)中

battery_main.psy = power_supply_register(&(dev->dev), &battery_main.psd, NULL);

battery_main
static struct battery_data battery_main = {
 .psd = {
  .name = "battery",
  .type = POWER_SUPPLY_TYPE_BATTERY,
  .properties = battery_props,
  .num_properties = ARRAY_SI
ZE(battery_props),
  .get_property = battery_get_property,
  },
 .BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING,
 .BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD,
 .BAT_PRESENT = 1,
 .BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION,
 .BAT_CAPACITY = -1,
 .BAT_batt_vol = 0,
 .BAT_batt_temp = 0,
 /* Dual battery */
 .status_smb = POWER_SUPPLY_STATUS_DISCHARGING,
 .capacity_smb = 50,
 .present_smb = 0,
 /* ADB CMD discharging */
 .adjust_power = -1,
};
           

battery_data

struct battery_data {
 struct power_supply_desc psd;
 struct power_supply *psy;
 int BAT_STATUS;
 int BAT_HEALTH;
 int BAT_PRESENT;
 int BAT_TECHNOLOGY;
 int BAT_CAPACITY;
 /* Add for Battery Service */
 int BAT_batt_vol;
 int BAT_batt_temp;
 /* Add for EM */
 int BAT_TemperatureR;
 int BAT_TempBattVoltage;
 int BAT_InstatVolt;
 int BAT_BatteryAverageCurrent;
 int BAT_BatterySenseVoltage;
 int BAT_ISenseVoltage;
 int BAT_ChargerVoltage;
 /* Dual battery */
 int status_smb;
 int capacity_smb;
 int present_smb;
 int adjust_power;
};
           

填充battery_get_property 回調函數

static int battery_get_property(struct power_supply *psy,
    enum power_supply_property psp, union power_supply_propval *val)
{
 int ret = 0;
 int fgcurrent = 0;
 bool b_ischarging = 0;
 struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
  val->intval = data->BAT_STATUS;
  break;
 case POWER_SUPPLY_PROP_HEALTH:
  val->intval = data->BAT_HEALTH;/* do not change before*/
  break;
 case POWER_SUPPLY_PROP_PRESENT:
  val->intval = data->BAT_PRESENT;/* do not change before*/
  break;
 case POWER_SUPPLY_PROP_TECHNOLOGY:
  val->intval = data->BAT_TECHNOLOGY;
  break;
 case POWER_SUPPLY_PROP_CAPACITY:
  val->intval = data->BAT_CAPACITY;
  break;
 case POWER_SUPPLY_PROP_CURRENT_NOW:
  b_ischarging = gauge_get_current(&fgcurrent);
  if (b_ischarging == false)
   fgcurrent = 0 - fgcurrent;
  val->intval = fgcurrent / 10;
  break;
 case POWER_SUPPLY_PROP_CURRENT_MAX:
  val->intval = 3000000;
  /* 3A */
  break;
.......
           

添加一個新的節點

添加一個新的節點主要就是填充sysfs以及battery_get_property

power_supply_sysfs.c
static struct device_attribute power_supply_attrs[] = {
 /* Properties of type `int' */
 POWER_SUPPLY_ATTR(status),
 POWER_SUPPLY_ATTR(charge_type),
 POWER_SUPPLY_ATTR(health),
 POWER_SUPPLY_ATTR(present),
 POWER_SUPPLY_ATTR(online),
 POWER_SUPPLY_ATTR(authentic),
 POWER_SUPPLY_ATTR(technology),
 POWER_SUPPLY_ATTR(cycle_count),
.......
#if 1
 POWER_SUPPLY_ATTR(BatteryVoltage),
 POWER_SUPPLY_ATTR(FGVoltage),
 POWER_SUPPLY_ATTR(BatteryCurrent),
 POWER_SUPPLY_ATTR(FGCurrent),
 POWER_SUPPLY_ATTR(BatInstTemp),
 POWER_SUPPLY_ATTR(BasSoc),
 POWER_SUPPLY_ATTR(PreSoc),
 POWER_SUPPLY_ATTR(BoardTemp),
 POWER_SUPPLY_ATTR(CPUTemp),
#endif
......

           

填充battery_get_property

static int battery_get_property(struct power_supply *psy,
    enum power_supply_property psp, union power_supply_propval *val)
{
 int ret = 0;
 int fgcurrent = 0;
 bool b_ischarging = 0;
 struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
  val->intval = data->BAT_STATUS;
  break;
 case POWER_SUPPLY_PROP_HEALTH:
  val->intval = data->BAT_HEALTH;/* do not change before*/
  break;

           

填充battery_get_property首先需要填充case 也就是enum power_supply_property 即相應properties

static enum power_supply_property battery_props[] = {
 POWER_SUPPLY_PROP_STATUS,
 POWER_SUPPLY_PROP_HEALTH,
 POWER_SUPPLY_PROP_PRESENT,
 POWER_SUPPLY_PROP_TECHNOLOGY,
......
 POWER_SUPPLY_PROP_BatteryVoltage,
 POWER_SUPPLY_PROP_FGVoltage,
 POWER_SUPPLY_PROP_BatteryCurrent,
 POWER_SUPPLY_PROP_FGCurrent,
 POWER_SUPPLY_PROP_BatInstTemp,
 POWER_SUPPLY_PROP_BasSoc,
 POWER_SUPPLY_PROP_PreSoc,
 POWER_SUPPLY_PROP_BoardTemp,
 POWER_SUPPLY_PROP_CPUTemp,
.......
           

之後填充battery_data的值

struct battery_data *data = container_of(psy->desc, struct battery_data,

struct battery_data {
 struct power_supply_desc psd;
 struct power_supply *psy;
 int BAT_STATUS;
 int BAT_HEALTH;
 int BAT_PRESENT;
 int BAT_TECHNOLOGY;
......
int BatteryVoltage;
 int FGVoltage;
 int BatteryCurrent;
 int FGCurrent;
 int BatInstTemp;
 int BasSoc;
 int PreSoc;
 int BoardTemp;
 int CPUTemp; 
.....
           

填充完畢後如下

static int battery_get_property(struct power_supply *psy,
    enum power_supply_property psp, union power_supply_propval *val)
{
 int ret = 0;
 int fgcurrent = 0;
 bool b_ischarging = 0;
 struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
  val->intval = data->BAT_STATUS;
.......
 case POWER_SUPPLY_PROP_BatteryVoltage:
  val->intval = data->BatteryVoltage;
  break;
 case POWER_SUPPLY_PROP_FGVoltage:
  val->intval = data->FGVoltage;
  break;
 case POWER_SUPPLY_PROP_BatteryCurrent:
  val->intval = data->BatteryCurrent;
  break;
 case POWER_SUPPLY_PROP_FGCurrent:
  val->intval = data->FGCurrent;
  break;
 case POWER_SUPPLY_PROP_BatInstTemp:
  val->intval = data->BatInstTemp;
  break;
 case POWER_SUPPLY_PROP_BasSoc:
  val->intval = data->BasSoc;
  break;
 case POWER_SUPPLY_PROP_PreSoc:
  val->intval = data->PreSoc;
  break;
 case POWER_SUPPLY_PROP_BoardTemp:
  val->intval = data->BoardTemp;
  break;
 case POWER_SUPPLY_PROP_CPUTemp:
  val->intval = data->CPUTemp;
  break; 
.......
           

最後需要去實作,想get的value

如在fg_drv_update_hw_status 線程中定期更新這些value

void fg_drv_update_hw_status(void)
{
 static bool fg_current_state;
 signed int chr_vol;
 int fg_current, fg_coulomb, bat_vol, plugout_status, tmp, bat_plugout_time;
 int fg_current_iavg;
 bool valid = false;
 static unsigned int cnt;

 struct battery_data *bat_data = &battery_main;
 static signed int previous_SOC = -1;
...........
bat_data->BatteryVoltage = bat_vol; // 瞬間電壓
 bat_data->FGVoltage = pmic_get_battery_voltage();    // FG電壓
 bat_data->BatteryCurrent = fg_current_iavg;    // 瞬時充電電流
 bat_data->FGCurrent = fg_current;    // FG電流
 bat_data->BatInstTemp = battery_meter_get_battery_temperature(); // 瞬間電池溫度
 bat_data->BasSoc = battery_get_bat_soc();    // 平均SOC
 bat_data->PreSoc = previous_SOC;    // 前SOC
 bat_data->CPUTemp = mtk_thermal_get_temp(MTK_THERMAL_SENSOR_CPU);    // cpu 溫度
 bat_data->BoardTemp = mtk_thermal_get_temp(MTK_THERMAL_SENSOR_AP);    // board 溫度
...........
           
參考:http://www.wowotech.net/pm_subsystem/psy_class_overview.html

繼續閱讀