2012年3月23日 星期五

Android Power Management

Overview

Android 的系統狀態可以分成以下五種(暫時忽略idle):


其中代表的意義如下

  • No Power        :  在沒有任何電源的時候( 包含battery/AC)
  • Off                   :  接上電源的時候(還沒按開機, G2)
  • Active              :  power on 之後( S0)
  • Early Suspend  : 當使用者過一段時間沒有動作後或是按下電源鍵,一般就是螢幕暗掉的時候
  • Suspend           : 當系統進入early suspend且沒有wake lock之後( S3 or S4)
以下將分開說明wakelock, earlysuspend/late resume 及suspend


wakelock

Android 的wakelock用途是要避免系統進入 low power mode ex. suspend or earlysuspend。舉個例來說,當我們在播電影的時候,使用者可能只會用"看"而不會去操作,為了避免系統的螢幕被關掉,我們就可以建立一個wakelock。

wakelock in Framework

Android 定義了4種互斥(四選一)的lock,下表分別列出其對於CPU, screen及keyboard的影響如下 :

Flag Value
CPU
Screen
Keyboard
On*
Off
Off
On
Dim
Off
On
Bright
Off
On
Bright
Bright

如果是partial_wake_lock, 系統的CPU永遠會是run的狀態(包含按下power button),其他的lock如果使用者按下power button依然會進入earlysuspend。另外,對於screen還有兩個額外的lock可以加入(如果與partial_wake_lock結合則不會有任何效果):


Flag Value
Description
Normal wake locks don't actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately.
If this flag is set, the user activity timer will be reset when the WakeLock is released, causing the illumination to remain on a bit longer. This can be used to reduce flicker if you are cycling between wake lock conditions.


在上層要使用wakelock可以參考:

PowerManager pm =(PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.new WakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK| PowerManager.ON_AFTER_RELEASE,TAG);
wl.acquire();  
// ... 
wl.release();



Earlysuspen/Lateresume

Framework

要進入earlysuspend的情況就是在Android裡
$ echo mem >/sys/power/state 
or
$ echo standby >/sys/power/state 
從earlysuspend回來的方法(lateresume)即是
$ echo on>/sys/power/state

以上圖來說,當使用者沒有動作時,Android的上層就會倒數,到0之後,就會由圖所示的call path一路透過call JNI 鏈結Android_os_Power.cpp再到Power.c最後直接下command給/sys/power/state 進入earlysuspend。此時,只會有panel, touch等等裝置會關閉(depends on kernel 實作)。

Kernel


在進入kernel後,依照上圖的sequence diagram跑,最後在early_suspend_work裡面會透過list_for_each_entry()去call 有註冊進earlysuspend list裡的driver,執行對應的earlysuspend code。


Suspend

wakelock in Kernel

上面介紹的 wakelock 是framework的部分,這裡要提到的wakelock是kernel的部分。當上層透過command新增一個wakelock給kernel的時候eg.
$ echo wakelock_name>/sys/power/wakelock
對於kernel來說,這是一個WAKE_LOCK_SUSPEND的wakelock,另外一種是WAKE_LOCK_IDLE在此忽略。

當新增了一個wakelock,他的流程可以參考上圖。Kernel的wakelock在以下的情況會去檢查是否仍然有wakelock存在
  • 解除wakelock ( $ echo wakelock_name >/sys/power/wake_unlock)
  • wakelock timer expired ( kernel裡面有一個timer會固定時間檢查是否有wakelock存在)

檢查的function是wake_has_wake_lock_locked(),當發現沒有 wakelock的時候就會呼叫suspend_work() 開始執行suspend的工作。相對應的suspend應該都會實作在arch/arm/mach-xxx裏頭。

相關的程式碼

  • frameworks/base/core/java/android/os
    • PowerManager.java
    • Power.java
  • frameworks/base/services/java/com/android/server
    • PowerManagerService.java
  • hardware/libhardware_legacy/power
    • power.c
  • frameworks/base/core/jni
    • android_os_Power.cpp
  • kernel/kernel/power
    • main.c( earlysuspend entry point)
    • wakelock.c
    • userwakelock.c
    • suspend.c
    • earlysuspend.c

Reference

Android Power Management, http://blog.csdn.net/hzdysymbol/article/details/4004791s3c2440電源管理主要問題及其解決方法, http://www.linuxforum.net/forum/printthread.php?Cat=&Board=embedded&main=725407&type=threadAndroid內核驅動——電源管理, http://www.docin.com/p-115475680.html#documentinfoa n d r o i d 休眠與喚醒, http://wenku.baidu.com/view/a4e4ab8483d049649b6658f3.htmlLinuxAndroid休眠喚醒對比(二), http://hi.baidu.com/ch_ff/blog/item/aa660bd8d4f6563811df9b96.htmlLinux Kernel and Android 休眠與喚醒, http://www.thinksrc.com/2010/04/18/suspend-cn.htmlAndroid Power Management, partial wake lock parts, http://tech-sjh.blogspot.com/2011/08/android-power-management-wake-lock.htmlLinuxsuspend機制的設計原理, http://www.4ucode.com/Study/Topic/1282150Tegra board support package for Android

Android LED Implementation


  1. 在config裡勾選
    CONFIG_NEW_LEDS=y
    CONFIG_LEDS_CLASS=y
    CONFIG_LEDS_GPIO=y
    CONFIG_LEDS_TRIGGERS=y
    CONFIG_LEDS_TRIGGER_TIMER=y 

  2. 在board-*.c 裡面建立platform_device 
    static struct gpio_led gpio_leds[] = {
     {
     .name = "WHITE_LED",//should change to your own
     .gpio = _GPIO_1,    //should change to your own
     .active_low = 1,
     .default_trigger = "timer", 
     },
    };
    
    static struct gpio_led_platform_data gpio_led_info = {
     .leds = gpio_leds,
     .num_leds = ARRAY_SIZE(gpio_leds),
    };
    
    static struct platform_device leds_gpio = {
     .name = "leds-gpio",
     .id = -1,
     .dev = {
     .platform_data = &gpio_led_info,
     },
    };
    //記得要在類似init加入-->
    platform_device_register(&leds_gpio);
    

  3. 實做platform_driver
    基本上依我的板子為例,platform_driver的實做已經在driver/leds/leds-gpio.c裡面做好了 不需要更改

  4. 修改LED HAL
    以我的版為例,修改的是libleds.c
    在
    static int open_lights(const struct hw_module_t *module, char const *name,
             struct hw_device_t **device)
    裡面新增了
    }else if(0 == strcmp(LIGHT_ID_BATTERY, name)){
        set_light = set_light_battery;
    }
    
    
    然後因為我的led是gpio led, 只支援on/off ,沒有brightness level, Android定義了不同的level會有不同的顏色或閃爍
    因此我的實做為
    
    
    static int set_light_battery(struct light_device_t *dev,
              struct light_state_t const *state)
    {
     int err = 0;
     int flashmode=state->flashMode;
     
     pthread_mutex_lock(&g_lock);
            
            //set flash
     if(flashmode==1){
      err = write_str("/sys/class/leds/PMU_LED/trigger","timer");
      err = write_int("/sys/class/leds/PMU_LED/delay_on",250);
      err = write_int("/sys/class/leds/PMU_LED/delay_off",150);
     
            }else if(state->color ==0xffffff00||state->color ==0xffff0000||state->color ==-256){
      err = write_str("/sys/class/leds/PMU_LED/trigger","none");
      err = write_int("/sys/class/leds/PMU_LED/brightness",0);
     }else if(state->color ==0xff00ff00||state->color ==0x0){
      err = write_str("/sys/class/leds/PMU_LED/trigger","none");
      err = write_int("/sys/class/leds/PMU_LED/brightness",255);
     }
     pthread_mutex_unlock(&g_lock);
    
     return err;
    }
    

2012年3月22日 星期四

Android Conditional Compilation( JAVA)

我們都知道JAVA其實是不支援條件式編譯的 例如在C/C++

#ifdef CONFIG_XXX_YYY
        ...
#ENDIF

$ gcc xxx.c -DCONFIG_XXX_YYY


所以我的方法是,在JAVA的地方產生一個JAVA檔,內容為

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class ConditionFlag {
    public static final Set  ConditionConfig;
    static {
        HashSet  config =new HashSet();
        config.put("CONFIG_XXX_YYY");
        ConditionConfig = Collections.unmodifiableSet(config);
    }
}

在其他的程式碼裡就可以:

if(ConditionFlag.ConditionConfig.Contains("CONFIG_XXX_YYY")){
     ...
}

這樣應該可以達到類似的目的了

repo 常用指令


  1. 取得source
    $ repo init -u [address] -b [branch]
  2. 更換remote branch
    $ repo init -b [branch]
  3. 建立/跳轉local branch
    $ repo start [branch] [dir]    or $ repo start --all [branch]
  4. 清除未進track的檔案
    $ repo forall -c git clean -fdD
  5. 清除未commit 檔案
    $ repo forall -c git reset --hard HEAD
  6. 取得 repository status
    $ repo status
  7. 清除未commit 檔案
    $ repo forall -c git reset --hard HEAD

completion

在Linux kernel  中我們可以使用completion來等待某樣事情完成
ex.


#include linux/completion.h
struct completion comp;
void module_init{

         //初始化my_completion, set comp.done=0
         init_completion(&comp);
}


ssiz​​e_t complete_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    //等待所有的comp 都被complete. 
    wait_for_completion(&comp);
    printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
    return 0; /* EOF */
}

ssiz​​e_t complete_write (struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
 
    complete(&comp);
    return count; /* succeed, to avoid retrial */
}

kzalloc()


  • static  inline  void  *kzalloc(size_t size, gfp_t flags): allocate memory. The memory is set to zero.
  • Flag: 
           size: 欲申請大小
           flags: same as kmalloc, see kmalloc



static inline void *kzalloc(size_t size, gfp_t flags)
{
return kmalloc(size, flags | __GFP_ZERO);
}

Reference:
include/linux/slab.h

kmalloc()


  • void * kmalloc (size_t size, int flags) : 在kernel配置記憶體位置
  • INCLUDE    :include/linux/slab.h
  • FLAG: 
          size:欲分配大小
          flags:

Action Modifiers( 通常使用後面提到的type modifier)    
__GFP_WAIT
The allocator can sleep.
__GFP_HIGH
The allocator can access emergency pools.
__GFP_IO
The allocator can start physical I/O.
__GFP_FS
The allocator can start filesystem I/O.
__GFP_COLD
The allocator should use cache cold pages.
__GFP_NOWARN
The allocator will not print failure warnings.
__GFP_REPEAT
The allocator will repeat the allocation if it fails, but the allocation can potentially fail. This depends upon the particular VM implementation.
__GFP_NOFAIL
The allocator will indefinitely repeat the allocation. The allocation cannot fail.
__GFP_NORETRY
The allocator will never retry if the allocation fails.
__GFP_COMP
Add compound page metadata. Used internally by thehugetlb code.
__GFP_ZERO
Add compound page metadata. Used internally by thehugetlb code.

__GFP_NOMEMALLOC
Don't use emergency reserves.
__GFP_HARDWALL
Enforce hardwall cpuset memory allocs.
__GFP_THISNODE
No fallback, no policies.
__GFP_RECLAIMABLE
Page is reclaimable.
__GFP_NOTRACK
Don't track with kmemcheck.
__GFP_NO_KSWAPD
__GFP_OTHER_NODE
On behalf of other node.

Example:

ptr = kmalloc(size, __GFP_WAIT | __GFP_IO | __GFP_FS);
 
Zone Modifiers ( 選擇要從哪分配空間 預設是從ZONE_NORMAL)

__GFP_DMA
Allocate only from ZONE_DMA
__GFP_HIGHMEM
Allocate from ZONE_HIGHMEM or ZONE_NORMAL
__GFP_DMA32

__GFP_MOVABLE

 

Type Flags( 定義了action 及 zone modifier)


GFP_ATOMIC
The allocation is high priority and must not sleep. This is the flag to use in interrupt handlers, in bottom halves, while holding a spinlock, and in other situations where you cannot sleep.
GFP_NOIO
This allocation can block, but must not initiate disk I/O. This is the flag to use in block I/O code when you cannot cause more disk I/O, which might lead to some unpleasant recursion.
GFP_NOFS
This allocation can block and can initiate disk I/O, if it must, but will not initiate a filesystem operation. This is the flag to use in filesystem code when you cannot start another filesystem operation.
GFP_KERNEL
This is a normal allocation and might block. This is the flag to use in process context code when it is safe to sleep. The kernel will do whatever it has to in order to obtain the memory requested by the caller. This flag should be your first choice.
GFP_USER
This is a normal allocation and might block. This flag is used to allocate memory for user-space processes.
GFP_HIGHUSER
This is an allocation from ZONE_HIGHMEM and might block. This flag is used to allocate memory for user-space processes.
GFP_DMA
This is an allocation from ZONE_DMA. Device drivers that need DMA-able memory use this flag, usually in combination with one of the above.

以下列出各Type modifier的組合:

GFP_ATOMIC
__GFP_HIGH
GFP_NOIO
__GFP_WAIT
GFP_NOFS
(__GFP_WAIT | __GFP_IO)
GFP_KERNEL
(__GFP_WAIT | __GFP_IO | __GFP_FS)
GFP_USER
(__GFP_WAIT | __GFP_IO | __GFP_FS)
GFP_HIGHUSER
(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HIGHMEM)
GFP_DMA
__GFP_DMA



建議的用法:

Situation
Solution
Process context, can sleep
Use GFP_KERNEL
Process context, cannot sleep
Use GFP_ATOMIC, or perform your allocations with GFP_KERNEL at an earlier or later point when you can sleep
Interrupt handler
Use GFP_ATOMIC
Softirq
Use GFP_ATOMIC
Tasklet
Use GFP_ATOMIC
Need DMA-able memory, can sleep
Use (GFP_DMA | GFP_KERNEL)
Need DMA-able memory, cannot sleep
Use (GFP_DMA | GFP_ATOMIC), or perform your allocation at an earlier point when you can sleep


Example:

char *reloc_lp0;
reloc_lp0 = kmalloc(size,GFP_KERNEL);
WARN_ON(!reloc_lp0);
if (!reloc_lp0) {
    pr_err("%s: Failed to allocate reloc_lp0\n",__func__);
   goto out;
}
...
kfree(reloc_lp0);

Reference:
include/linux/gfp.h
http://www.makelinux.net/books/lkd2/ch11lev1sec4
http://www.google.com.tw/url?sa=t&rct=j&q=&esrc=s&source=web&cd=5&ved=0CF8QFjAE&url=http%3A%2F%2Fant.comm.ccu.edu.tw%2Fcourse%2F96_Driver%2F0_Lectures%2FChap7-Getting%2520Hold%2520of%2520Memory_milk.ppt&ei=TMFqT_ysHqWfmQW5obyWBg&usg=AFQjCNGvVEk9-Io481khM_SqoFd-WsvwqQ&sig2=qvOIAsidZzvw15vpq0JlrQ