Android模擬器學framework和driver之battery & backlight—–1.battery in linux

在linux中battery驅動主要是去處理供電方面的東西,大傢看下driver在bsp中的路徑就可以知道,android模擬器使用的goldfish內核中battery驅動的位置是:
android/common/drivers/power/goldfish_battery.c
目前手機,平板電腦日益普及,在嵌入式領域battery的續航能力也一直制約著手機等嵌入式設備的發展,iphone比android手機做的好多瞭,希望android可以再處理上下功夫,趕超apple,廢話不多說,這裡battery主要是處理,電池供電、插上充電器充電、USB供電等事情的發生,還有就是一些電池的信息管理,比如說電量、溫度等狀態可以使用戶知道。
OK,這邊我們主要是使用goldfish中的battery驅動來分析一下linux中的power模塊是如何工作的。
在這之前我們首先要來看一下power_supply這個device driver 子系統是如何建立的,這邊我們涉及到的代碼都在/common/drivers/power/下:
power_supply_core.c
power_supply_sysfs.c
goldfish_battery.c
power_supply_core.c是power_supple subsystem的核心函數,在power_supply子系統在linux啟動的時候會先調用到裡面的餓init函數:
[cpp]
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; 

 
subsys_initcall(power_supply_class_init); 

這個函數比較簡單首先是在class中創建瞭一個power_supply的class,啟動模擬器後可以看到在sys/class/下會有一個power_supply文件夾生成,然後是
power_supply_class->dev_uevent = power_supply_uevent;這句話把power_supply_uevent掛到power_supply_class的dev_uevent上,這裡說明下,就是說power_supply子系統都是使用uevent機制把信息傳到user space的,當battery的狀態發生改變的時候會向用戶空間上報一個uevent,這樣的話用戶空間就可以知道什麼時候去抓信息。
這個power_supply_uevent是個回調函數,被定義在power_supply_sysfs.c中:
[cpp]
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->dev) { 
        dev_dbg(dev, "No power supply yet\n"); 
        return ret; 
    } 
 
    dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); 
 
    ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name); 
    if (ret) 
        return ret; 
 
    prop_buf = (char *)get_zeroed_page(GFP_KERNEL); 
    if (!prop_buf) 
        return -ENOMEM; 
 
    for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { 
        struct device_attribute *attr; 
        char *line; 
 
        attr = &power_supply_static_attrs[j]; 
 
        ret = power_supply_show_static_attrs(dev, attr, prop_buf); 
        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, "Static 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; 
    } 
 
    dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); 
 
    for (j = 0; j < psy->num_properties; j++) { 
        struct device_attribute *attr; 
        char *line; 
 
        attr = &power_supply_attrs[psy->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; 
 
        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; 

稍微有點長,不怕,我們慢慢來分析,這裡隻是開頭,什麼都沒有應該比較好分析,
我們抓重點,首先是ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);這句話添加瞭uevent的一些環境變量,這裡添加瞭POWER_SUPPLY_NAME,接著都是些分配內存的代碼,還有就是把power_supply_properties中註冊的所有屬性都添加成環境變量,這樣的話當battery的某個屬性發生變化的時候都會通報到上層讓用戶知道。
這裡uevent我不做詳細介紹,網上資料也一大堆,後面講到jni層的時候我也會做補充用戶層是如何去開啟一個socket來抓kernel中的uevent的。
回到我們的power_supply_core.c中,在linux起來的時候隻有調用到core中的class_init函數,其他的函數都是為我們的driver服務的,
現在我們來看下我們的goldfish_battery驅動,打開android/common/drivers/power/goldfish_battery.c,一般我喜歡先看init函數,
[cpp]
static int __init goldfish_battery_init(void) 

    return platform_driver_register(&goldfish_battery_device); 

很簡單,註冊瞭platform_driver,同學們看到瞭這個函數第一反應應該是去找device_register函數,以為隻有這2個函數匹配瞭才算在linux系統中註冊瞭platform 設備,那麼在哪呢?在我們goldfish的板級文件中有註冊device,這邊很奇怪,其實我沒有找到具體的goldfish-battery的device_register但是在板級文件中有一個pdev_bus的註冊
arch/arm/mach-goldfish/pdev_bus.c
[cpp]
static void goldfish_pdev_worker(struct work_struct *work); 
 
static uint32_t pdev_bus_base; 
static uint32_t pdev_bus_irq; 
static LIST_HEAD(pdev_bus_new_devices); 
static LIST_HEAD(pdev_bus_registered_devices); 
static LIST_HEAD(pdev_bus_removed_devices); 
static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker); 
 
 
static void goldfish_pdev_worker(struct work_struct *work) 

    int ret; 
    struct pdev_bus_dev *pos, *n; 
 
    list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) { 
        list_del(&pos->list); 
        platform_device_unregister(&pos->pdev); 
        kfree(pos); 
    } 
    list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) { 
        list_del(&pos->list); 
        <span style="color: rgb(255, 0, 0); ">ret = platform_device_register(&pos->pdev);</span> 
        if(ret) { 
            printk("goldfish_pdev_worker failed to register device, %s\n", pos->pdev.name); 
        } 
        else { 
            printk("goldfish_pdev_worker registered %s\n", pos->pdev.name); 
        } 
        list_add_tail(&pos->list, &pdev_bus_registered_devices); 
    } 

可以看下上面這個代碼中,在工作隊列中遍歷所有的pdev鏈表,調用platform_device_register把設備註冊進去,之後就會調用到我們的probe函數瞭。
———————————————————————————
probe函數主要還是做一些初始化,probe的意思就是探測的意思,就是試著去初始化(我是這麼理解的),
[cpp]
static int goldfish_battery_probe(struct platform_device *pdev) 

    int ret; 
    struct resource *r; 
    struct goldfish_battery_data *data; 
 
    data = kzalloc(sizeof(*data), GFP_KERNEL); 
    if (data == NULL) { 
        ret = -ENOMEM; 
        goto err_data_alloc_failed; 
    } 
    spin_lock_init(&data->lock); 
 
    data->battery.properties = goldfish_battery_props; 
    data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props); 
    data->battery.get_property = goldfish_battery_get_property; 
    data->battery.name = "battery"; 
    data->battery.type = POWER_SUPPLY_TYPE_BATTERY; 
 
    data->ac.properties = goldfish_ac_props; 
    data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props); 
    data->ac.get_property = goldfish_ac_get_property; 
    data->ac.name = "ac"; 
    data->ac.type = POWER_SUPPLY_TYPE_MAINS; 
 
    r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
    if (r == NULL) { 
        printk(KERN_ERR "%s: platform_get_resource failed\n", pdev->name); 
        ret = -ENODEV; 
        goto err_no_io_base; 
    } 
    data->reg_base = IO_ADDRESS(r->start – IO_START); 
 
    data->irq = platform_get_irq(pdev, 0); 
    if (data->irq < 0) { 
        printk(KERN_ERR "%s: platform_get_irq failed\n", pdev->name); 
        ret = -ENODEV; 
        goto err_no_irq; 
    } 
 
    ret = request_irq(data->irq, goldfish_battery_interrupt, IRQF_SHARED, pdev->name, data); 
    if (ret) 
        goto err_request_irq_failed; 
 
    ret = power_supply_register(&pdev->dev, &data->ac); 
    if (ret) 
        goto err_ac_failed; 
 
    ret = power_supply_register(&pdev->dev, &data->battery); 
    if (ret) 
        goto err_battery_failed; 
 
    platform_set_drvdata(pdev, data); 
    battery_data = data; 
 
    GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); 
    return 0; 
 
err_battery_failed: 
    power_supply_unregister(&data->ac); 
err_ac_failed: 
    free_irq(data->irq, data); 
err_request_irq_failed: 
err_no_irq: 
err_no_io_base: 
    kfree(data); 
err_data_alloc_failed: 
    return ret; 

首先來看幾個結構體,其實個人認為,linux驅動給開發人員做的很多就是填充結構體,因為基本框架linux已經實現瞭,我們要做的是把這個大框架填滿,使這個框架有血有肉,這樣才能正常工作。
首先是goldfish_battery_data 這個結構體:
[cpp]
struct goldfish_battery_data { 
    uint32_t reg_base; 
    int irq; 
    spinlock_t lock; 
 
    struct power_supply battery; 
    struct power_supply ac; 
}; 

這個結構體中我們關註的是power_supply這個結構體,別的沒什麼好說的,中斷啊,寄存器地址啊,自旋鎖什麼的。
[cpp]
struct power_supply { 
    const char *name; 
    enum power_supply_type type; 
    enum power_supply_property *properties; 
    size_t num_properties; 
 
    char **supplied_to; 
    size_t num_supplicants; 
 
    int (*get_property)(struct power_supply *psy, 
                enum power_supply_property psp, 
                union power_supply_propval *val); 
    void (*external_power_changed)(struct power_supply *psy); 
 
    /* For APM emulation, think legacy userspace. */ 
    int use_for_apm; 
 
    /* private */ 
    struct device *dev; 
    struct work_struct changed_work; 
 
#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; 
#endif 
}; 

這部分我們對照著probe中的代碼一起分析,主要還是填充這個結構體:
[cpp]
data->battery.properties = goldfish_battery_props; 
data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props); 
data->battery.get_property = goldfish_battery_get_property; 
data->battery.name = "battery"; 
data->battery.type = POWER_SUPPLY_TYPE_BATTERY; 

首先是對battery的屬性信息的填寫:
[cpp]
static enum power_supply_property goldfish_battery_props[] = { 
    POWER_SUPPLY_PROP_STATUS, 
    POWER_SUPPLY_PROP_HEALTH, 
    POWER_SUPPLY_PROP_PRESENT, 
    POWER_SUPPLY_PROP_TECHNOLOGY, 
    POWER_SUPPLY_PROP_CAPACITY, 
}; 

這邊電池的信息由status health present等,我們比較關註的還是電池的電量,capacity,
然後是屬性的數目,不多說瞭,下面第三個參數比較重要 data->battery.get_property = goldfish_battery_get_property;
[cpp]
static int goldfish_battery_get_property(struct power_supply *psy, 
                 enum power_supply_property psp, 
                 union power_supply_propval *val) 

    struct goldfish_battery_data *data = container_of(psy, 
        struct goldfish_battery_data, battery); 
    int ret = 0; 
 
    switch (psp) { 
    case POWER_SUPPLY_PROP_STATUS: 
        //modify charge to discharge 
        //val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS); 
        val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 
        //—–modify end 
        break; 
    case POWER_SUPPLY_PROP_HEALTH: 
        val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH); 
        break; 
    case POWER_SUPPLY_PROP_PRESENT: 
        val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT); 
        break; 
    case POWER_SUPPLY_PROP_TECHNOLOGY: 
        val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 
        break; 
    case POWER_SUPPLY_PROP_CAPACITY: 
        //modify capacity from 50 per to 20 per by Jay 
        //val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); 
        val->intval = global_brightness; 
        //—-modify end 
        break; 
    default: 
        ret = -EINVAL; 
        break; 
    } 
 
    return ret; 

驅動中就是通過這個函數去實時得到電池的信息,然後使用uevent傳到user space,我們重點講下這個函數是怎麼工作的。
大傢註意到這裡這個函數的參數,struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val
第一個參數是我們最重要的data struct,第二個參數是屬性的類型,選擇我們要得到的屬性,第三個參數就是這個屬性的值是多少,比如說電池電量是百分之多少。
這個函數是被掛在power_supply結構體中的get_property上的,它是在哪被回調呢?我們先在這裡打個問號,我們知道這裡隻是在一些結構體中填充好瞭我們battery的信息,至於在什麼時候回調,什麼時候使用,什麼時候上傳uevent,我們後面會講到,留意下!!
繼續回到probe函數,下面是
[cpp]
data->battery.name = "battery"; 
data->battery.type = POWER_SUPPLY_TYPE_BATTERY; 

名字和類型的填寫。
下面就是跟硬件相關的東西瞭,一般的device是會用中斷的方法去觸發,這裡goldfish的內核使用的是PC的中斷資源。
[cpp]
data->irq = platform_get_irq(pdev, 0); 
if (data->irq < 0) { 
    printk(KERN_ERR "%s: platform_get_irq failed\n", pdev->name); 
    ret = -ENODEV; 
    goto err_no_irq; 

 
ret = request_irq(data->irq, goldfish_battery_interrupt, IRQF_SHARED, pdev->name, data); 

得到中斷好,然後申請中斷,掛好中斷觸發的函數,這裡發生中斷後會做一系列的事情,我們也先放一下,先來看看下面的power_supply註冊函數:
[cpp]
ret = power_supply_register(&pdev->dev, &data->ac); 
if (ret) 
    goto err_ac_failed; 
 
ret = power_supply_register(&pdev->dev, &data->battery); 
if (ret) 
    goto err_battery_failed; 
 
platform_set_drvdata(pdev, data); 
battery_data = data; 

power_supply_register函數在power_supply_core中被定義
[cpp]
int power_supply_register(struct device *parent, struct power_supply *psy) 

    int rc = 0; 
 
    psy->dev = device_create(power_supply_class, parent, 0, psy, 
                 "%s", psy->name); 
    if (IS_ERR(psy->dev)) { 
        rc = PTR_ERR(psy->dev); 
        goto dev_create_failed; 
    } 
 
    INIT_WORK(&psy->changed_work, power_supply_changed_work); 
 
    rc = power_supply_create_attrs(psy); 
    if (rc) 
        goto create_attrs_failed; 
 
    rc = power_supply_create_triggers(psy); 
    if (rc) 
        goto create_triggers_failed; 
 
    power_supply_changed(psy); 
 
    goto success; 
 
create_triggers_failed: 
    power_supply_remove_attrs(psy); 
create_attrs_failed: 
    device_unregister(psy->dev); 
dev_create_failed: 
success: 
    return rc; 

首先是時候device_create函數在class下創建設備驅動,傳入的第一個參數就是我們的power_supply class
然後是初始化瞭工作隊列INIT_WORK(&psy->changed_work, power_supply_changed_work);
oK,我們來看下這個工作隊列的回調函數,power_supply_changed_work
[cpp]
static void power_supply_changed_work(struct work_struct *work) 

    struct power_supply *psy = container_of(work, struct power_supply, 
                        changed_work); 
 
    dev_dbg(psy->dev, "%s\n", __func__); 
 
    class_for_each_device(power_supply_class, NULL, psy, 
                  __power_supply_changed_work); 
 
    power_supply_update_leds(psy); 
 
    kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); 

這裡就是調用瞭2個函數,第一個 class_for_each_device(power_supply_class, NULL, psy,
     __power_supply_changed_work);
它遍歷瞭我們power_supply_class上的所有節點,用psy作為參數調用瞭__power_supply_changed_work函數,__power_supply_changed_work函數的作用是匹配我們的驅動
[cpp]
static int __power_supply_changed_work(struct device *dev, void *data) 

    struct power_supply *psy = (struct power_supply *)data; 
    struct power_supply *pst = dev_get_drvdata(dev); 
    int i; 
 
    for (i = 0; i < psy->num_supplicants; i++) 
        if (!strcmp(psy->supplied_to[i], pst->name)) { 
            if (pst->external_power_changed) 
                pst->external_power_changed(pst); 
        } 
    return 0; 

之後再調用到瞭我們最最最最關鍵的地方kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);這裡就是註冊瞭kobject uevent事件,當我們的屬性發生變化的時候會把uevent傳給用戶空間。
所以說,調用到我們的工作隊列的時候就會向用戶空間上報event,這個動作被封裝在
[cpp]
void power_supply_changed(struct power_supply *psy) 

    dev_dbg(psy->dev, "%s\n", __func__); 
 
    schedule_work(&psy->changed_work); 

之後會被使用到。
回到我們的power_supply_register函數中
rc = power_supply_create_attrs(psy);被定義在power_supply_sysfs.c中
[cpp]
int power_supply_create_attrs(struct power_supply *psy) 

    int rc = 0; 
    int i, j; 
 
    for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { 
        rc = device_create_file(psy->dev, 
                &power_supply_static_attrs[i]); 
        if (rc) 
            goto statics_failed; 
    } 
 
    for (j = 0; j < psy->num_properties; j++) { 
        rc = device_create_file(psy->dev, 
                &power_supply_attrs[psy->properties[j]]); 
        if (rc) 
            goto dynamics_failed; 
    } 
 
    goto succeed; 
 
dynamics_failed: 
    while (j–) 
        device_remove_file(psy->dev, 
               &power_supply_attrs[psy->properties[j]]); 
statics_failed: 
    while (i–) 
        device_remove_file(psy->dev, &power_supply_static_attrs[i]); 
succeed: 
    return rc; 

這邊遍歷瞭我們的屬性數組,把每個屬性在我們的驅動中聲稱文件系統,調用到device_create_flie函數,我們運行模擬器在power_supply/battery/下會發現我們填進去的屬性值生成的所有文件。
這裡介紹完畢,回到我們的goldfish驅動文件,最後就是我們的中斷函數瞭。
[cpp]
static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) 

    unsigned long irq_flags; 
    struct goldfish_battery_data *data = dev_id; 
    uint32_t status; 
 
    spin_lock_irqsave(&data->lock, irq_flags); 
 
    /* read status flags, which will clear the interrupt */ 
    status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); 
    status &= BATTERY_INT_MASK; 
 
    if (status & BATTERY_STATUS_CHANGED) 
        power_supply_changed(&data->battery); 
    if (status & AC_STATUS_CHANGED) 
        power_supply_changed(&data->ac); 
 
    spin_unlock_irqrestore(&data->lock, irq_flags); 
    return status ? IRQ_HANDLED : IRQ_NONE; 

這裡我什麼也不看隻想看一句話
[cpp]
if (status & BATTERY_STATUS_CHANGED) 
    power_supply_changed(&data->battery); 
if (status & AC_STATUS_CHANGED) 
    power_supply_changed(&data->ac); 

之前講過,就是power_supply_changed函數觸發瞭向用戶空間上報event事件的代碼。OK ,android goldfish battery驅動就講到這邊,我看android模擬器的電池老是動啊動的很是不爽所以我在get_property函數中充電狀態改成瞭不是充電狀態,然後把電池電量設置為恒定的,嘿嘿,這樣的話就不會動瞭,哈哈。下面貼張圖,有圖有真相

 

下面一篇我們會講到android framework中jni對驅動的封裝,好累啊,一口氣寫完battery driver分析。

 

摘自 zhangjie201412的專欄

發佈留言