android待机流程分析

以下是amlogic 905x android6.0的待机日志

假待机:
[   79.159234@0] request_suspend_state: sleep (0->3) at 78988163869 (2015-01-01 00:01:24.258131660 UTC)
[   79.162712@0] request_suspend_state,164,old_sleep=0,new_state=3
[   79.168682@0] early_suspend: call handlers
[   79.201103@0] avmute set to 2
[   79.301870@0] hdmitx: system: HDMITX: early suspend
[   79.301915@0] hdmitx: system: unmux DDC for gpio read edid
[   79.308968@0] 
[   79.308968@0] vdac_enable: on:0,module_sel:8
[   79.354569@0] fb: osd_suspended
[   79.354611@0] sysled:  early suspend ok
[   79.355889@0] gxbb_pm: meson_system_early_suspend
[   79.360544@0] early_suspend: sync
[   80.080436@0] PM: suspend entry 2015-01-01 00:01:25.179274911 UTC
[   80.080994@0] PM: Syncing filesystems ... done.
[   80.110489@0] Freezing user space processes ... (elapsed 0.269 seconds) done.
[   80.380308@0] Freezing remaining freezable tasks ... (elapsed 0.003 seconds) done.
[   80.392719@0] RTL871X:  suspend start
[   80.424913@0] RTL871X: nolinked power save leave
[   80.436464@0] RTL871X: rtw_cmd_thread(wlan0) stop_req:1, break
[   80.445590@0] RTL871X: rtw_dev_unload: driver not in IPS
[   80.476912@0] RTL871X: rtw_dev_unload: driver not in IPS
[   80.478154@0] RTL871X: rtw suspend success in 80 ms
[   80.614705@0] aml_snd_card: enter aml_suspend_pre
[   80.614889@0] aml_snd_card: no audio pin_ctrl to suspend
[   80.630767@0] aml_T9015_audio_suspend!
[   80.630944@0] aml_snd_card: enter aml_suspend_post
[   80.634393@0] meson_uart c11084c0.serial: pull up rts
[   80.639082@0] amvecm: suspend module
[   80.642597@0] DI: di: di_suspend
[   80.646704@0] sysled: module suspend ok
[   80.650166@0] PM: suspend of devices complete after 259.894 msecs
[   80.655553@0] gxbb_pm: enter meson_pm_prepare!
[   80.665042@0] PM: late suspend of devices complete after 5.337 msecs
[   80.682488@0] PM: noirq suspend of devices complete after 16.474 msecs
[   80.683457@0] Disabling non-boot CPUs ...
[   80.687612@0] gxbb_pm: enter meson_pm_suspend!
[   80.687612@0] gxbb_pm: late_suspend: call handlers
[   80.687612@0] gxbb_pm: late_suspend: sync
bl30 get wakeup sources!
process command 00000006
bl30 enter suspend!
cpu clk suspend rate 100000000
suspend_counter: 1
Enter ddr suspend
first time suspend
ddr suspend time: 1886us
store restore gp0 pll
process command 00000001
CEC cfg:0x002f
set vddee to 0x035cmv
089 kern log_addr:0x04
rx stat:00, tx stat:00
ping_cec_ll_tx:TX_ERROR
Set cec log_addr:0x04, ADDR0:14
真待机:
[   68.214993@1] BT_RADIO going: off
[   68.215020@1] BCM_BT: going OFF
[   68.442588@0] BT_RADIO going: on
[   68.442752@0] BCM_BT: going ON
[   68.687745@1] aml_spdif_dai: aml_hw_iec958_init,runtime->rate=48000, same source mode(1)
[   68.690825@1] aml_spdif_dai: share the same clock
[   68.694954@1] aml_spdif_dai: iec958 mode PCM16
[   68.699346@1] aml_audio_hw: IEC958 16bit
[   68.703504@1] hdmitx: audio: aout notify rate 48000
[   68.708042@1] hdmitx: audio: aout notify size 16
[   68.712906@1] hdmitx: audio: no update
[   68.716294@1] aml_snd_m8_card aml_m8_snd.46: i2s/958 same source
[   68.751379@1] aml_snd_m8_card aml_m8_snd.46: I2S playback enable
[   68.751759@1] aml_snd_m8_card aml_m8_snd.46: IEC958 playback enable
[   69.368257@0] meson_uart c11084c0.serial: ttyS1 use xtal(8M) 24000000 change 1500000 to 1500000
[   69.373019@0] meson_uart c11084c0.serial: ttyS1 use xtal(8M) 24000000 change 1500000 to 1500000
[   69.380607@0] meson_uart c11084c0.serial: ttyS1 use xtal(8M) 24000000 change 1500000 to 115200
[   69.910159@0] meson_uart c11084c0.serial: ttyS1 use xtal(8M) 24000000 change 115200 to 1500000
[   69.914035@0] meson_uart c11084c0.serial: ttyS1 use xtal(8M) 24000000 change 1500000 to 1500000
[   70.874014@0] SysRq : Emergency Remount R/O
[   70.880471@1] EXT4-fs (mmcblk0p14): re-mounted. Opts: (null)
[   70.883091@1] EXT4-fs (mmcblk0p3): re-mounted. Opts: (null)
[   70.889695@1] EXT4-fs (mmcblk0p8): re-mounted. Opts: (null)
[   70.891972@1] EXT4-fs (zram0): re-mounted. Opts: (null)
[   70.904583@1] Emergency Remount complete
[   70.974799@1] avmute set to 2
[   71.768553@0] aml_snd_m8_card aml_m8_snd.46: I2S playback disable
[   71.769016@0] aml_snd_m8_card aml_m8_snd.46: IEC958 playback disable
[   71.775430@0] aml_spdif_dai: share the same clock
[   71.779982@0] aml_audio_hw: IEC958 16bit
[   71.784033@0] hdmitx: audio: aout notify rate 48000
[   71.788698@0] hdmitx: audio: aout notify size 16
[   71.793337@0] hdmitx: audio: no update
[   72.101818@0] aml_snd_card: no audio pin_ctrl to shutdown
[   72.102215@0] sysled: module shutdown ok
[   72.105939@0] reboot: Power down
INFO:    PSCI Affinity Map:
INFO:      AffInst: Level 0, MPID 0x0, State ON
INFO:      AffInst: Level 0, MPID 0x1, State ON
INFO:      AffInst: Level 0, MPID 0x2, State OFF
INFO:      AffInst: Level 0, MPID 0x3, State OFF
bl31 reboot reason: 0x108
bl31 reboot reason: 0x108
system cmd  0.
bl30 get wakeup sources!
process command 00000006
bl30 enter suspend!
cpu clk suspend rate 1000000000
suspend_counter: 1
Enter ddr suspend
first time suspend
ddr suspend time: 1886us
store restore gp0 pll
process command 00000001
CEC cfg:0x002f
set vddee to 0x035cmv
089 kern log_addr:0x04
rx stat:00, tx stat:00
ping_cec_ll_tx:TX_ERROR
Set cec log_addr:0x04, ADDR0:14

android上层通过设置环境变量去关机

    public static void lowLevelShutdown() {
        SystemProperties.set("sys.powerctl", "shutdown");
    }

直接触发init.rc去执行

on property:sys.powerctl=*
    powerctl ${sys.powerctl}
./code/system/core/init/keywords.h:80:    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)

int do_powerctl(int nargs, char **args);

./code/system/core/init/builtins.cpp:581:int do_powerctl(int nargs, char **args)
int do_powerctl(int nargs, char **args)
{
    char command[PROP_VALUE_MAX];
    int res;
    int len = 0;
    int cmd = 0;
    const char *reboot_target;

    res = expand_props(command, args[1], sizeof(command));
    if (res) {
        ERROR("powerctl: cannot expand '%s'\n", args[1]);
        return -EINVAL;
    }

    if (strncmp(command, "shutdown", 8) == 0) {
        cmd = ANDROID_RB_POWEROFF;
        len = 8;
    } else if (strncmp(command, "reboot", 6) == 0) {
        cmd = ANDROID_RB_RESTART2;
        len = 6;
    } else {
        ERROR("powerctl: unrecognized command '%s'\n", command);
        return -EINVAL;
    }

    if (command[len] == ',') {
        reboot_target = &command[len + 1];
    } else if (command[len] == '\0') {
        reboot_target = "";
    } else {
        ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
        return -EINVAL;
    }

    return android_reboot(cmd, 0, reboot_target);
}

do_powerctl根据cmd标记flags

   if (strncmp(command, "shutdown", 8) == 0) {
        cmd = ANDROID_RB_POWEROFF;
        len = 8;
    } else if (strncmp(command, "reboot", 6) == 0) {
        cmd = ANDROID_RB_RESTART2;
        len = 6;

用android_reboot(cmd, 0, reboot_target);执行命令
其中

#define ANDROID_RB_RESTART  0xDEAD0001
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
int android_reboot(int cmd, int flags UNUSED, const char *arg)
{
    int ret;

    sync();
    remount_ro();

    switch (cmd) {
        case ANDROID_RB_RESTART:
            ret = reboot(RB_AUTOBOOT);
            break;

        case ANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

    return ret;
}
./bionic/libc/bionic/reboot.cpp:35

extern "C" int __reboot(int, int, int, void*);

int reboot(int mode) {
  return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
}
./bionic/libc/arch-arm64/syscalls/__reboot.S

/* Generated by gensyscalls.py. Do not edit. */

#include <private/bionic_asm.h>

ENTRY(__reboot)
    mov     x8, __NR_reboot
    svc     #0

    cmn     x0, #(MAX_ERRNO + 1)
    cneg    x0, x0, hi
    b.hi    __set_errno_internal

    ret
END(__reboot)
.hidden __reboot
=》最后会进入Kernel
如
./prebuilts/ndk/8/platforms/android-9/arch-arm/usr/include/sys/reboot.h:42:#define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF
则进入内核的kernel/reboot.c


SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        void __user *, arg)
{

    case LINUX_REBOOT_CMD_POWER_OFF:
        kernel_power_off();
        do_exit(0);
        break;
void kernel_power_off(void)
{
    kernel_shutdown_prepare(SYSTEM_POWER_OFF);
    if (pm_power_off_prepare)
        pm_power_off_prepare();
    migrate_to_reboot_cpu();
    syscore_shutdown();
    pr_emerg("Power down\n");
    kmsg_dump(KMSG_DUMP_POWEROFF);
    machine_power_off();
}
EXPORT_SYMBOL_GPL(kernel_power_off);
Process.c (arch\arm64\kernel):void machine_power_off(void)

void machine_power_off(void)
{
    local_irq_disable();
    machine_shutdown();
    if (pm_power_off) 关电源
        pm_power_off();
}

关闭多核

void machine_shutdown(void)
{
#ifdef CONFIG_SMP
    smp_send_stop();
#endif
}

void smp_send_stop(void)
{
    unsigned long timeout;

    if (num_online_cpus() > 1) {
        cpumask_t mask;

        cpumask_copy(&mask, cpu_online_mask);
        cpu_clear(smp_processor_id(), mask);

        smp_cross_call(&mask, IPI_CPU_STOP);
    }

    /* Wait up to one second for other CPUs to stop */
    timeout = USEC_PER_SEC;
    while (num_online_cpus() > 1 && timeout--)
        udelay(1);

    if (num_online_cpus() > 1)
        pr_warning("SMP: failed to stop secondary CPUs\n");
}
函数指针pm_power_off是与平台相关的指针, 只是一个函数指针,而且做了全局操作,整个kernel都可以调用它,amlogic平台是如下方法设置的
./common/drivers/amlogic/reset/gxbb_poweroff.c:152:     pm_power_off = do_aml_poweroff;


static void do_aml_poweroff(void)
{
    /* TODO: Add poweroff capability */
    __invoke_psci_fn_smc(0x82000042, 1, 0, 0);
    __invoke_psci_fn_smc(psci_function_id_poweroff,
                0, 0, 0);
}

即最后设置寄存器:

static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1,
                     u64 arg2)
{
    register long x0 asm("x0") = function_id;
    register long x1 asm("x1") = arg0;
    register long x2 asm("x2") = arg1;
    register long x3 asm("x3") = arg2;
    asm volatile(
            __asmeq("%0", "x0")
            __asmeq("%1", "x1")
            __asmeq("%2", "x2")
            __asmeq("%3", "x3")
            "smc    #0\n"
        : "+r" (x0)
        : "r" (x1), "r" (x2), "r" (x3));

    return function_id;
}

系统没有wake_lock时,以下timer会检测执行回调:
static void suspend(struct work_struct *work)
{
int ret;
int entry_event_num;
struct timespec ts_entry, ts_exit;

if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
    if (debug_mask & DEBUG_SUSPEND)
        pr_info("suspend: abort suspend\n");
    return;
}

entry_event_num = current_event_num;
if (debug_mask & DEBUG_SUSPEND)
    pr_info("suspend: sys_sync...");
sys_sync();
if (debug_mask & DEBUG_SUSPEND)
    pr_info("done.\n");
if (debug_mask & DEBUG_SUSPEND)
    pr_info("suspend: enter suspend\n");
getnstimeofday(&ts_entry);
ret = pm_suspend(requested_suspend_state);
getnstimeofday(&ts_exit);

if (debug_mask & DEBUG_EXIT_SUSPEND) {
    struct rtc_time tm;
    rtc_time_to_tm(ts_exit.tv_sec, &tm);
    pr_info("suspend: exit suspend, ret = %d (%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
        ret,
        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
        tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);
}

if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {
    ++suspend_short_count;

    if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {
        suspend_backoff();
        suspend_short_count = 0;
    }
} else {
    suspend_short_count = 0;
}

if (current_event_num == entry_event_num) {
    if (debug_mask & DEBUG_SUSPEND)
        pr_info("suspend: pm_suspend returned with no event\n");

}
wake_lock_timeout(&unknown_wakeup, 5 * HZ);

}

static DECLARE_WORK(suspend_work, suspend);

static void expire_wake_locks(unsigned long data)
{
    long has_lock;
    unsigned long irqflags;
    if (debug_mask & DEBUG_EXPIRE)
        pr_info("expire_wake_locks: start\n");
    spin_lock_irqsave(&list_lock, irqflags);
    if (debug_mask & DEBUG_SUSPEND)
        print_active_locks(WAKE_LOCK_SUSPEND);
    has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
    if (debug_mask & DEBUG_EXPIRE)
        pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
    if (has_lock == 0)
        queue_work(suspend_work_queue, &suspend_work);
    spin_unlock_irqrestore(&list_lock, irqflags);
}

static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
该timer会在多个地方用到,在激活锁的函数中注册用于超时锁到期后检测系统的有效锁状态,如果系统不存在有效锁了则启动suspend进程回调pm_suspend。

上层修改的对应的设备节点之后
正常时是

root@p212:/sys/power # cat wake_unlock                                         
KeyEvents PowerManagerService.WakeLocks 
root@p212:/sys/power # cat wake_lock                                           
PowerManagerService.Display 
root@p212:/sys/power # cat state                                               
mem disk
root@p212:/sys/power # 
const char * const OLD_PATHS[] = {
    "/sys/android_power/acquire_partial_wake_lock",
    "/sys/android_power/release_wake_lock",
};

const char * const NEW_PATHS[] = {
    "/sys/power/wake_lock",
    "/sys/power/wake_unlock",
};

执行以下代码:

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
               const char *buf, size_t n)
{
    suspend_state_t state;
    int error;

    error = pm_autosleep_lock();
    if (error)
        return error;

    if (pm_autosleep_state() > PM_SUSPEND_ON) {
        error = -EBUSY;
        goto out;
    }

    state = decode_state(buf, n);
    if (state < PM_SUSPEND_MAX) {
#ifdef CONFIG_EARLYSUSPEND
        if (state == PM_SUSPEND_ON || state == PM_SUSPEND_MEM) {
            error = 0;
            request_suspend_state(state);
        }
#else
        error = pm_suspend(state);
#endif
    }
    else if (state == PM_SUSPEND_MAX)
        error = hibernate();
    else
        error = -EINVAL;

 out:
    pm_autosleep_unlock();
    return error ? error : n;
}

重点执行这个func request_suspend_state

void request_suspend_state(suspend_state_t new_state)
{
    unsigned long irqflags;
    int old_sleep;

    spin_lock_irqsave(&state_lock, irqflags);
    old_sleep = state & SUSPEND_REQUESTED;
    if (debug_mask & DEBUG_USER_STATE) {
        struct timespec ts;
        struct rtc_time tm;
        getnstimeofday(&ts);
        rtc_time_to_tm(ts.tv_sec, &tm);
        pr_info("request_suspend_state: %s (%d->%d) at %lld "
            "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
            new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
            requested_suspend_state, new_state,
            ktime_to_ns(ktime_get()),
            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
            tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
    }
    pr_info("%s,%d,old_sleep=%d,new_state=%d\n", __func__, __LINE__,
           old_sleep, new_state);
    if (!old_sleep && new_state != PM_SUSPEND_ON) {
        state |= SUSPEND_REQUESTED;
        queue_work(suspend_work_queue, &early_suspend_work);
    } else if (old_sleep && new_state == PM_SUSPEND_ON) {
        state &= ~SUSPEND_REQUESTED;
        wake_lock(&main_wake_lock);
        queue_work(suspend_work_queue, &late_resume_work);
    }
    requested_suspend_state = new_state;
    spin_unlock_irqrestore(&state_lock, irqflags);
}

于是我们就看到了以上打印

pr_info("request_suspend_state: %s (%d->%d) at %lld "
            "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
            new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
            requested_suspend_state, new_state,
            ktime_to_ns(ktime_get()),
            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
            tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec)
    pr_info("%s,%d,old_sleep=%d,new_state=%d\n", __func__, __LINE__,
           old_sleep, new_state);
[  735.994473@0] request_suspend_state: sleep (0->3) at 735823580141 (2015-01-01 00:34:28.725320516 UTC)
[  735.998034@0] request_suspend_state,164,old_sleep=0,new_state=3

表示进下如下流程:

if (!old_sleep && new_state != PM_SUSPEND_ON) {
        state |= SUSPEND_REQUESTED;
        queue_work(suspend_work_queue, &early_suspend_work);
    } 

修改标记位,执行工作队列回调

        queue_work(suspend_work_queue, &early_suspend_work);
static void early_suspend(struct work_struct *work)
{
    struct early_suspend *pos;
    unsigned long irqflags;
    int abort = 0;

    mutex_lock(&early_suspend_lock);
    spin_lock_irqsave(&state_lock, irqflags);
    if (state == SUSPEND_REQUESTED)
        state |= SUSPENDED;
    else
        abort = 1;
    spin_unlock_irqrestore(&state_lock, irqflags);

    if (abort) {
        if (debug_mask & DEBUG_SUSPEND)
            pr_info("early_suspend: abort, state %d\n", state);
        mutex_unlock(&early_suspend_lock);
        goto abort;
    }

    if (debug_mask & DEBUG_SUSPEND)
        pr_info("early_suspend: call handlers\n");
    list_for_each_entry(pos, &early_suspend_handlers, link) {
        if (pos->suspend != NULL)
            pos->suspend(pos);
    }
    mutex_unlock(&early_suspend_lock);

    if (debug_mask & DEBUG_SUSPEND)
        pr_info("early_suspend: sync\n");

    sys_sync();
 abort:
    spin_lock_irqsave(&state_lock, irqflags);
    if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
        wake_unlock(&main_wake_lock);
    spin_unlock_irqrestore(&state_lock, irqflags);
}

接着走

    if (debug_mask & DEBUG_SUSPEND)
        pr_info("early_suspend: call handlers\n");
    list_for_each_entry(pos, &early_suspend_handlers, link) {
        if (pos->suspend != NULL)
            pos->suspend(pos);
    }

执行注册在early_suspend_handlers的所有回调函数

之后释放锁

    if (debug_mask & DEBUG_SUSPEND)
        pr_info("early_suspend: sync\n");

    sys_sync();
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值