Android電池架構分析
此文基於博文 http://wangzhigang2.iteye.com/blog/1270925稍作補充,主要新增 kernel流程的分析
BatteryService實現了一個UevenObserver mUEventObserver。
uevent是Linux核心用來向用戶空間主動上報事件的機制,對於JAVA程序來說,只實現UEventObserver的虛擬函式onUEvent,然後註冊即可。
Java程式碼
1. private UEventObserver mUEventObserver = new UEventObserver(){
2. @Override
3. public void onUEvent(UEventObserver.UEvent event){
4. update();
5. }
6. }
BatteryService
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();
}
Java程式碼
1. private synchronized final void update(){
2. native_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_property。power_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);
這個就不用解釋了。裝置節點和這些巨集對應起來了。