1. 程式人生 > >Android電池架構分析

Android電池架構分析

此文基於博文 http://wangzhigang2.iteye.com/blog/1270925稍作補充,主要新增 kernel流程的分析

 BatteryService實現了一個UevenObserver mUEventObserver ueventLinux核心用來向用戶空間主動上報事件的機制,對於JAVA序來說,只實現UEventObserver的虛擬函式onUEvent,然後註冊即可。
Java
程式碼
1. private UEventObserver mUEventObserver = new UEventObserver(){
2. @Override
3. public void onUEvent(UEventObserver.UEvent event){
4. update();
5. }
6. }
BatteryService

只關注power_supply的事件,所以在建構函式註冊:
Java
程式碼

public BatteryService(Context context) {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
//mUEventObserver.startObserving("SUBSYSTEM=power_supply");
String hwNoBatteryStr = SystemProperties.get("hw.nobattery");
hwNoBattery = Boolean.parseBoolean(hwNoBatteryStr);
if (!hwNoBattery)
// Slog.d(TAG,"[baker] Battery service class hwNOBatter = false \n");
mUEventObserver.startObserving("SUBSYSTEM=power_supply");
// set initial status
update();
}

這裡需要在 init.rc中設定 hw.nobattery屬性為 false ,以啟動監聽 Uevent 執行緒。 (2)update() update讀取sysfs檔案做到同步取得電池資訊,然後根據讀到的狀態更新 BatteryService的成員變數,並廣播一個Intent來通知其它關注電源狀態的元件。kernelpower_supply事件上報時,mUEventObserver呼叫 update()函式,然後update呼叫native_updatesysfs中讀取相關狀態com_android_server_BatteryService.cpp):
Java
程式碼
1. private synchronized final void update(){
2. native_update(); //
android_server_BatteryService_update()
3. }
(3)
sysfs Linux驅動driver維護著儲存電池資訊的一組檔案sysfs,供應用程式獲取電源相關狀態: #defineAC_ONLINE_PATH "/sys/class/power_supply/ac/online" AC 電源連線狀態 #define USB_ONLINE_PATH"/sys/class/power_supply/usb/online" USB 電源連線狀態 #define BATTERY_STATUS_PATH"/sys/class/power_supply/battery/status"充電狀態 #define BATTERY_HEALTH_PATH"/sys/class/power_supply/battery/health"電池狀態 #define BATTERY_PRESENT_PATH"/sys/class/power_supply/battery/present"使用狀態 #define BATTERY_CAPACITY_PATH"/sys/class/power_supply/battery/capacity"電池level #define BATTERY_VOLTAGE_PATH"/sys/class/power_supply/battery/batt_vol"電池電壓 #define BATTERY_TEMPERATURE_PATH"/sys/class/power_supply/battery/batt_temp"電池溫度 #define BATTERY_TECHNOLOGY_PATH"/sys/class/power_supply/battery/technology"電池技術當電池狀態發生變化時,driver會更新這些檔案。以上分析來自 http://wangzhigang2.iteye.com/blog/1270925,但沒有具體分析 Kernel的處理流程。下面接著補充分享下
(4)kernel
(以我除錯的 twl4030_charger.c為例)中斷函式:
static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
{
struct twl4030_bci *bci = arg;
//int gpio_value = 0;
printk("[baker] Enter %s :%s ---- \n",__FILE__,__func__);
dev_dbg(bci->dev, "CHG_PRES irq\n");
ac_charger_state=gpio_get_value(CHARGER_STATE_GPIO);
ac_charger_state = (~ ac_charger_state) & 0x01;
power_supply_changed(&bci->ac);
power_supply_changed(&bci->usb);
printk("[baker] ac_charger_state = %d ---\n",ac_charger_state);
//ac_charger_state = 1 ;
return IRQ_HANDLED;
}
Kernel層,檢測到中斷後(充電器插入或拔出、電量的變化等),呼叫 kobject_uevent上報 Uevent
twl4030_charger_interrupt
power_supply_changed (
power_supply_changed_work)
kobject_uevent() // kobject_uevent.c
power_supply_uevent() // power_supply_sysfs.c
power_supply_show_property()
add_uevent_var()
kobject_uevent中,如果獲取不到某個屬性,將有可能導致 Uevent不能上報,致 Onuvent不能執行。跟蹤 kobject_uevent()程式碼,最終會在 kobject_uevent_env呼叫 kset指定的 dev_uevent(即 uevent_ops->uevent)設定環境變數。而 kset指定的 uevent是在 power_supply_class_init中配置。在設定環境變數後,將呼叫 netlink_broadcast上報 uevent
// power_supply_core.c
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;
return 0;
}
//power_supply_sysfs.c
power_supply_uevent中,注意 power_supply_show_propertypower_supply_show_property twl4030_charger.c 中填寫,當獲取屬性失敗時,返回 -61 ,Uevent將不能上報。
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
{
.....
for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) {
...
ret = power_supply_show_static_attrs(dev, attr, prop_buf);
if (ret < 0)
goto out;
....
ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
kfree(attrname);
if (ret)
goto out;
}
dev_dbg(dev, "%zd dynamic props\n", psy->num_properties);
for (j = 0; j < psy->num_properties; j++) {
....
ret = power_supply_show_property(dev, attr, prop_buf);
if (ret == -ENODEV) {
/* 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;
....
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);
}

power_supply_sysfs.c中最終會建立uevent

static struct device_attributepower_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(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(current_max),
         POWER_SUPPLY_ATTR(current_now),
         POWER_SUPPLY_ATTR(current_avg),
         POWER_SUPPLY_ATTR(power_now),
         POWER_SUPPLY_ATTR(power_avg),
         POWER_SUPPLY_ATTR(charge_full_design),
         POWER_SUPPLY_ATTR(charge_empty_design),
         POWER_SUPPLY_ATTR(charge_full),
         POWER_SUPPLY_ATTR(charge_empty),
         POWER_SUPPLY_ATTR(charge_now),
         POWER_SUPPLY_ATTR(charge_avg),
         POWER_SUPPLY_ATTR(charge_counter),
         POWER_SUPPLY_ATTR(energy_full_design),
         POWER_SUPPLY_ATTR(energy_empty_design),
         POWER_SUPPLY_ATTR(energy_full),
         POWER_SUPPLY_ATTR(energy_empty),
         POWER_SUPPLY_ATTR(energy_now),
         POWER_SUPPLY_ATTR(energy_avg),
         POWER_SUPPLY_ATTR(capacity),
         POWER_SUPPLY_ATTR(capacity_level),
         POWER_SUPPLY_ATTR(temp),
         POWER_SUPPLY_ATTR(temp_ambient),
         POWER_SUPPLY_ATTR(time_to_empty_now),
         POWER_SUPPLY_ATTR(time_to_empty_avg),
         POWER_SUPPLY_ATTR(time_to_full_now),
         POWER_SUPPLY_ATTR(time_to_full_avg),
         POWER_SUPPLY_ATTR(type),
         /*Properties of type `const char *' */
         POWER_SUPPLY_ATTR(model_name),
         POWER_SUPPLY_ATTR(manufacturer),
         POWER_SUPPLY_ATTR(serial_number),
};

static ssize_t power_supply_show_property(structdevice *dev,
                                              
  struct device_attribute *attr,
                                                 char *buf) {
         staticchar *type_text[] = {
                   "Battery","UPS", "Mains", "USB",
                   "USB_DCP","USB_CDP", "USB_ACA"
         };
         staticchar *status_text[] = {
                   "Unknown","Charging", "Discharging", "Not charging","Full"
         };
         staticchar *charge_type[] = {
                   "Unknown","N/A", "Trickle", "Fast"
         };
         staticchar *health_text[] = {
                   "Unknown","Good", "Overheat", "Dead", "Overvoltage",
                   "Unspecifiedfailure", "Cold",
         };
         staticchar *technology_text[] = {
                   "Unknown","NiMH", "Li-ion", "Li-poly", "LiFe","NiCd",
                   "LiMn"
         };
         staticchar *capacity_level_text[] = {
                   "Unknown","Critical", "Low", "Normal", "High","Full"
         };
         ssize_tret = 0;
         structpower_supply *psy = dev_get_drvdata(dev);
         constptrdiff_t off = attr - power_supply_attrs;
         unionpower_supply_propval value;


         if(off == POWER_SUPPLY_PROP_TYPE)
                   value.intval= psy->type;
         else
                   ret= psy->get_property(psy, off, &value);


         if(ret < 0) {
                   if(ret == -ENODATA)
                            dev_dbg(dev,"driver has no data for `%s' property\n",
                                     attr->attr.name);
                   elseif (ret != -ENODEV)
                            dev_err(dev,"driver failed to report `%s' property\n",
                                     attr->attr.name);
                   returnret;
         }


         if(off == POWER_SUPPLY_PROP_STATUS)
                   returnsprintf(buf, "%s\n", status_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
                   returnsprintf(buf, "%s\n", charge_type[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_HEALTH)
                   returnsprintf(buf, "%s\n", health_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_TECHNOLOGY)
                   returnsprintf(buf, "%s\n", technology_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
                   returnsprintf(buf, "%s\n", capacity_level_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_TYPE)
                   returnsprintf(buf, "%s\n", type_text[value.intval]);
         elseif (off >= POWER_SUPPLY_PROP_MODEL_NAME)
                   returnsprintf(buf, "%s\n", value.strval);


         returnsprintf(buf, "%d\n", value.intval);

}

其中這裡

      POWER_SUPPLY_ATTR(status),
        
POWER_SUPPLY_ATTR(charge_type),
         POWER_SUPPLY_ATTR(health),

          ……

是可能會建立的uevent節點如:/sys/class/power_supply/battery/status

但是什麼情況下驅動往裡面送資料的呢?

power_supply_show_property()函式。

ssize_t ret = 0;
        
structpower_supply *psy = dev_get_drvdata(dev);
         constptrdiff_t off = attr - power_supply_attrs;

       ……

通過offer來定位它最終把資料寫好相對應的節點下。

battery驅動裡會用到一些巨集,這些巨集的位置和 power_supply_attrs[] 對應的位置是一樣的。

power_supply.h

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_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_CURRENT_MAX,
         POWER_SUPPLY_PROP_CURRENT_NOW,
         POWER_SUPPLY_PROP_CURRENT_AVG,
         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_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_LEVEL,
         POWER_SUPPLY_PROP_TEMP,
         POWER_SUPPLY_PROP_TEMP_AMBIENT,
         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 */
         /*Properties of type `const char *' */
         POWER_SUPPLY_PROP_MODEL_NAME,
         POWER_SUPPLY_PROP_MANUFACTURER,
         POWER_SUPPLY_PROP_SERIAL_NUMBER,
};

所以off = attr - power_supply_attrs;   

只要attr=POWER_SUPPLY_ATTR(status)    減去 power_supply_attrs.就可以算出off的值了。

然後->

if (off == POWER_SUPPLY_PROP_STATUS)
                  
returnsprintf(buf, "%s\n", status_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
                   returnsprintf(buf, "%s\n", charge_type[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_HEALTH)
                   returnsprintf(buf, "%s\n", health_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_TECHNOLOGY)
                   returnsprintf(buf, "%s\n", technology_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
                   returnsprintf(buf, "%s\n", capacity_level_text[value.intval]);
         elseif (off == POWER_SUPPLY_PROP_TYPE)
                   returnsprintf(buf, "%s\n", type_text[value.intval]);
         elseif (off >= POWER_SUPPLY_PROP_MODEL_NAME)
                   returnsprintf(buf, "%s\n", value.strval);
         returnsprintf(buf, "%d\n", value.intval);

這個就不用解釋了。裝置節點和這些巨集對應起來了。