【android睡眠唤醒 】linux 唤醒时间分析

 

原文:https://blog.csdn.net/frank_zyp/article/details/64123416 
一、唤醒流程: 
  MTK平台唤醒流程是从power键或者其他按键按下开始,本文以mt6753n平台为例,通过分析kernel log来看驱动中整个唤醒的流程,上层的唤醒流程后续再分析。 
1、按键按下 
  当按键按下时会出发中断,从而回调中断的处理函数,从kernel log从会有以下log吐出:

01-02 12:27:04.500571    70    70 E [  940.825599]: (3)[70:pmic_thread][name:pmic&][PMIC] [pwrkey_int_handler] Press pwrkey 0
01-02 12:27:04.500593    70    70 E [  940.825621](3)[70:pmic_thread][name:kpd&]: Power Key generate, pressed=1
01-02 12:27:04.500694    70    70 E [  940.825722](3)[70:pmic_thread][name:hal_kpd&]: kpd: (pressed) HW keycode =116 using PMIC
1
2
3
  在代码中体现如下:

./kernel-3.18/drivers/misc/mediatek/power/mt6735/pmic.c
void pwrkey_int_handler(void)
{
    PMICLOG("[pwrkey_int_handler] Press pwrkey %d\n", pmic_get_register_value(PMIC_PWRKEY_DEB));

#if defined(CONFIG_MTK_KERNEL_POWER_OFF_CHARGING)
    if (get_boot_mode() == KERNEL_POWER_OFF_CHARGING_BOOT)
        timer_pre = sched_clock();
#endif
#if defined(CONFIG_MTK_FPGA)
#else
    kpd_pwrkey_pmic_handler(0x1);
#endif
}

  接下来调用kpd_pwrkey_pmic_handler:

void kpd_pwrkey_pmic_handler(unsigned long pressed)
{
    kpd_print("Power Key generate, pressed=%ld\n", pressed);
    if (!kpd_input_dev) {
        kpd_print("KPD input device not ready\n");
        return;
    }
    kpd_pmic_pwrkey_hal(pressed);
#if (defined(CONFIG_ARCH_MT8173) || defined(CONFIG_ARCH_MT8163))
    if (pressed) /* keep the lock while the button in held pushed */
        wake_lock(&pwrkey_lock);
    else /* keep the lock for extra 500ms after the button is released */
        wake_lock_timeout(&pwrkey_lock, HZ/2);
#endif
}
  接下来调用kpd_pmic_pwrkey_hal:

void kpd_pmic_pwrkey_hal(unsigned long pressed)
{
#ifdef CONFIG_KPD_PWRKEY_USE_PMIC
    if (!kpd_sb_enable) {
        //通过input子系统向上层上报键值
        input_report_key(kpd_input_dev, kpd_dts_data.kpd_sw_pwrkey, pressed);
        input_sync(kpd_input_dev);
        if (kpd_show_hw_keycode) {
            kpd_print(KPD_SAY "(%s) HW keycode =%d using PMIC\n",
                   pressed ? "pressed" : "released", kpd_dts_data.kpd_sw_pwrkey);
        }
        /*ZH CHEN*/
        /*aee_powerkey_notify_press(pressed);*/
    }
#endif
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  到这里从按键按下触发中断到回调中断函数,最后通过input子系统向上层上报键值。

2、进程解冻,系统开始唤醒 
  当按键按下后,系统开始唤醒,一些sensor(gsensor/psensor/lsensor/gyro/msensor等)会开始进入resume流程,log如下:

01-02 12:27:04.518694   873   873 W [  940.843722]: (0)[873:system_server]MSENSOR af7133e_af8133i_resume
01-02 12:27:04.522326   873   873 W [  940.847354]: (2)[873:system_server][Gsensor] bmi160_acc_resume
01-02 12:27:04.534504   873   873 D [  940.859532]: (2)[873:system_server][name:ltr579&][ALS/PS] ltr579_i2c_resume
......
1
2
3
4
  当这些进程解冻完成后,在kernel log中会有标志性的log吐出:

01-02 12:27:04.544117   401   401 W [  940.869145]: (0)[401:fuelgauged]Restarting tasks ...
1
3、LCM开始唤醒: 
  当系统唤醒完成后,LCM也会开始进入唤醒状态,当LCM唤醒完成之后会退出唤醒状态,这个过程都有标志性的log吐出:

......
01-02 12:27:05.076832   272   272 W [  940.936520]: (4)[272:surfaceflinger][name:mtkfb&]DISP/MTKFB [FB Driver] enter late_resume
......
01-02 12:27:05.413783   272   272 W [  941.273471]: (0)[272:surfaceflinger][name:mtkfb&]DISP/MTKFB [FB Driver] leave late_resume
......
1
2
3
4
5
  其中在enter late_resume和leave late_resume中会执行LCM的唤醒操作,这部分的耗时与屏的时序有关,当屏上电和下参数的时间越短,手机唤醒亮屏的时间也就越短,代码如下:

//./kernel-3.18/drivers/misc/mediatek/video/common/mtkfb.c
static void mtkfb_blank_resume(void)
{
    int ret = 0;
    MSG_FUNC_ENTER();
    if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL)
        return;

    PRNWARN("[FB Driver] enter late_resume\n");

#ifdef CONFIG_SINGLE_PANEL_OUTPUT
    is_early_suspended = false;
#endif

    ret = primary_display_resume();
    if (ret) {
        DISPERR("primary display resume failed\n");
        return;
    }

    PRNWARN("[FB Driver] leave late_resume\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   primary_display_resume()函数比较多,包含显示模块的上电、初始化、lcm的上电、resume等的过程:

int primary_display_resume(void)
{
    ......
    DISPMSG("dpmanager path power on[begin]\n");
    dpmgr_path_power_on(pgc->dpmgr_handle, CMDQ_DISABLE);
    DISPMSG("dpmanager path power on[end]\n");
    ......
    DISPMSG("[POWER]dpmanager re-init[begin]\n");
    dpmgr_init...
    DISPMSG("[POWER]dpmanager re-init[end]\n");
    ......

    DISPMSG("[POWER]lcm resume[begin]\n");
    disp_lcm_resume(pgc->plcm);
    DISPMSG("[POWER]lcm resume[end]\n");
    ......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  下面重点看disp_lcm_resume:

int disp_lcm_resume(disp_lcm_handle *plcm)
{
    LCM_DRIVER *lcm_drv = NULL;

    DISPFUNC();
    if (_is_lcm_inited(plcm)) {
        lcm_drv = plcm->drv;
        //调用lcm驱动代码中的resume_power
        if (lcm_drv->resume_power)
            lcm_drv->resume_power();
        //调用lcm驱动代码中的lcm_resume
        if (lcm_drv->resume) {
            lcm_drv->resume();
        } else {
            DISPERR("FATAL ERROR, lcm_drv->resume is null\n");
            return -1;
        }

        return 0;
    }

    DISPERR("lcm_drv is null\n");
    return -1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4、背光亮 
  当LCM初始化完成后会调用背光的驱动,通过上层传入的level_1024来设置背光的亮度,从而亮屏

01-02 12:27:05.527109   487   487 W [  941.386797]: (4)[487:AALServiceMain][name:ddp_pwm&][PWM] disp_pwm_set_backlight_cmdq(id = 0x1, level_1024 = 358), old = 0
1
  从上面kernel log的唤醒流程中可以看出,整个唤醒的流程主要包括:power键按下、进程解冻、late_resume、背光亮这几步,所以优化唤醒的时间可以从这几个方面出发优化。
--------------------- 

 在文章MTK 唤醒时间分析中分析了内核中的主要的亮屏重要阶段,此篇文章结合上层的log一起来分析下整个系统的亮屏流程。整个流程可以分为如下几个部分:

  (1)power键(home键)产生并上报(在input子系统中已经介绍);

  (2)上层接收到到键值,PowerManagerService执行相关处理;

  (3)PMS更新全局电源状态,并开始唤醒屏幕和背光,并通知各个模块(如图形绘制Keyguard);

  (4)调用surfaceflinger开始执行屏幕的和背光的操作;

  (5)调用到驱动中的late_resume(在MTK 唤醒时间分析中已经介绍);

一、PMS接收键值过程及更新全局状态:

  当PowerMangerService接收到power键值后,会打印出调用的堆栈信息如下:

01-01 20:08:44.042224   981  1162 D PowerManagerService:    |----com.android.server.power.PowerManagerService.wakeUpNoUpdateLocked(PowerManagerService.java:1750)
01-01 20:08:44.042242   981   995 D NetworkPolicy: no need to update restrict data rules for uid 1000
01-01 20:08:44.042271   981  1162 D PowerManagerService:    |----com.android.server.power.PowerManagerService.wakeUpInternal(PowerManagerService.java:1741)
01-01 20:08:44.042282   981   995 D NetworkPolicy: no need to update restrict power rules for uid 1000
01-01 20:08:44.042374   981  1162 D PowerManagerService:    |----com.android.server.power.PowerManagerService.-wrap43(PowerManagerService.java)
01-01 20:08:44.042415   981  1162 D PowerManagerService:    |----com.android.server.power.PowerManagerService$BinderService.wakeUp(PowerManagerService.java:4261)
01-01 20:08:44.042458   981  1162 D PowerManagerService:    |----android.os.PowerManager.wakeUp(PowerManager.java:769)
01-01 20:08:44.042505   981  1162 D PowerManagerService:    |----com.android.server.policy.PhoneWindowManager.wakeUp(PhoneWindowManager.java:7421)
01-01 20:08:44.042548   981  1162 D PowerManagerService:    |----com.android.server.policy.PhoneWindowManager.interceptKeyBeforeQueueing(PhoneWindowManager.java:6874)
01-01 20:08:44.042579   981  1162 D PowerManagerService:    |----com.android.server.wm.InputMonitor.interceptKeyBeforeQueueing(InputMonitor.java:465)
01-01 20:08:44.042609   981  1162 D PowerManagerService:    |----com.android.server.input.InputManagerService.interceptKeyBeforeQueueing(InputManagerService.java:1897)
  从上面的log中可以清晰的看到键值被InputManagerService.java的interceptKeyBeforeQueueing方法接收到:

private WindowManagerCallbacks mWindowManagerCallbacks;    
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}    
  interceptKeyBeforeQueueing会调用到PhoneManagerService(PhoneWindowManager.java)中相同的方法如下:

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    case KeyEvent.KEYCODE_VOLUME_DOWN:
    case KeyEvent.KEYCODE_VOLUME_UP:
    ...//一些列的键值判断处理
    case KeyEvent.KEYCODE_POWER: {
        result &= ~ACTION_PASS_TO_USER;
        isWakeKey = false; // wake-up will be handled separately
        if (down) {
            if(!isKeysTest()){
                interceptPowerKeyDown(event, interactive);
            }else{
                sendKey(KeyEvent.KEYCODE_POWER);
            }
        }
        break;
    }
    ......
    case KeyEvent.KEYCODE_WAKEUP: {
        result &= ~ACTION_PASS_TO_USER;
        isWakeKey = true;
        break;
    }  
    if (isWakeKey) {
        wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
    }
 
    return result;
}
    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
        final boolean theaterModeEnabled = isTheaterModeEnabled();
        if (!wakeInTheaterMode && theaterModeEnabled) {
            return false;
        }
        if (theaterModeEnabled) {
            Settings.Global.putInt(mContext.getContentResolver(),
                    Settings.Global.THEATER_MODE_ON, 0);
        }
 
        mPowerManager.wakeUp(wakeTime, reason);
        return true;
    }
  上面代码经过一些列的键值判断后,最终会调用到wakeup代码如下:

    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
        final boolean theaterModeEnabled = isTheaterModeEnabled();
        if (!wakeInTheaterMode && theaterModeEnabled) {
            return false;
        }
        if (theaterModeEnabled) {
            Settings.Global.putInt(mContext.getContentResolver(),
                    Settings.Global.THEATER_MODE_ON, 0);
        }
        mPowerManager.wakeUp(wakeTime, reason);
        return true;
    }
  PowerManager.java中的wakeUp比较简单:

    public void wakeUp(long time, String reason) {
        try {
            mService.wakeUp(time, reason, mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
  最终会调用到PowerManagerService中的wakeUp做真正的唤醒工作:

        public void wakeUp(long eventTime, String reason, String opPackageName) {
            if (eventTime > SystemClock.uptimeMillis()) {
                throw new IllegalArgumentException("event time must not be in the future");
            }
            try {
                wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
            int opUid) {
        synchronized (mLock) {
            if (mIPOShutdown && reason != PowerManager.WAKE_UP_REASON_SHUTDOWN)
                return;
            if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
                updatePowerStateLocked();//更新电源状态
            }
        }
    }
  onWakefulnessChangeStarted中会调用notify的onWakefulnessChangeStarted发送亮屏广播,通知亮屏。

    private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
            String opPackageName, int opUid) {
        Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
        try {
        ......
            switch (mWakefulness) {
                case WAKEFULNESS_ASLEEP:
                    Slog.i(TAG, "Waking up from sleep (uid " + reasonUid +")...");
                    break;
                case WAKEFULNESS_DREAMING:
                    Slog.i(TAG, "Waking up from dream (uid " + reasonUid +")...");
                    break;
                case WAKEFULNESS_DOZING:
                    Slog.i(TAG, "Waking up from dozing (uid " + reasonUid +")...");
                    break;
            }
            mLastWakeTime = eventTime;
            setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
        .....
    }
    private void setWakefulnessLocked(int wakefulness, int reason) {
        if (mWakefulness != wakefulness) {
            mWakefulness = wakefulness;
            mWakefulnessChanging = true;
            mDirty |= DIRTY_WAKEFULNESS;
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);//调用到notify中
        }
    }
  在Notifier.java中与AMS,window,input进行交互,通知各模块手机状态发生了改变,根据屏幕状态各自进行处理, 最后发送亮灭屏广播, 通知关心的模块,Notify.java中的onWakefulnessChangeStarted方法列举一些重要的内容如下:

    public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
        ......
        mActivityManagerInternal.onWakefulnessChanged(wakefulness); //与AMS交互处理
        
        mBatteryStats.noteInteractive(interactive);//唤醒battery状态  
        
        handleEarlyInteractiveChange(); //处理前期交互模式改变 ,如锁屏 Keyguard等
        ......
    }
  当上述各个模块初始化完成后会重新调用到wakeUpInternal中的updatePowerStateLocked更新全局电源状态,并开始执行DisplayPowerController.java中的方法。

    private void updatePowerState() {
        int state;
        ......
        int brightness = PowerManager.BRIGHTNESS_DEFAULT;
        
        // Apply the proximity sensor.
        if (mProximitySensor != null) {
            //获取psensor的状态
        }
        if (mScreenOffBecauseOfProximity) {
            state = Display.STATE_OFF;
        }
        //获取自动背光的状态值后,调用animateScreenBrightness开始执行背光操作
        animateScreenBrightness(brightness,
                        slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
        //判断亮屏前是否有未完成的任务,如图形是否绘制成功等
        // Grab a wake lock if we have unfinished business.
        if (!finished && !mUnfinishedBusiness) {
            if (DEBUG) {
                Slog.d(TAG, "Unfinished business...");
            }
            mCallbacks.acquireSuspendBlocker();
            mUnfinishedBusiness = true;
        }
        // Release the wake lock when we have no unfinished business.
        if (finished && mUnfinishedBusiness) {
            if (DEBUG) {
                Slog.d(TAG, "Finished business...");
            }
            mUnfinishedBusiness = false;
            mCallbacks.releaseSuspendBlocker();
        }
        // Record if dozing for future comparison.
        mDozing = state != Display.STATE_ON;
    }
  updatePowerState()中有很多的操作,上面的代码只列举了部分重要阶段的代码,上面的任务完成后,下面会调用到LocalDisplayAdapter.java真正的开始亮屏和亮背光的操作。

    LocalDisplayAdapter.java
        public Runnable requestDisplayStateLocked(final int state, final int brightness)
            private void setDisplayState(int state)
                blockScreenOn会等待windws绘制完成,unblockScreenOn
            private void setDisplayBrightness(int brightness)
                SurfaceControl.setDisplayPowerMode调用到surfacefligner的setPowerMode
二、surfaceflinger亮屏和亮背光

  在LocalDisplayAdapter中会通过相应的接口调用到surfacefligner的操作:

    //frameworks/base/core/java/android/view/SurfaceControl.java   
    public static void setDisplayPowerMode(IBinder displayToken, int mode) {
        if (displayToken == null) {
            throw new IllegalArgumentException("displayToken must not be null");
        }
        nativeSetDisplayPowerMode(displayToken, mode);
    }
    这样又调用到native层的接口:

//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw,
             int mode, bool stateLockHeld) {
    ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
            this);
    int32_t type = hw->getDisplayType();
    int currentMode = hw->getPowerMode();
 
    hw->setPowerMode(mode);
    if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
        ALOGW("Trying to set power mode for virtual display");
        return;
    }
    //...这里做了很多工作,上面只列举了setPowerMode的部分
}
void HWCDisplay::setPowerMode(const int32_t& mode)
{
    // screen blanking based on early_suspend in the kernel
    HWC_LOGI("Display(%" PRId64 ") SetPowerMode(%d)", m_disp_id, mode);
    m_power_mode = mode;
    DisplayManager::getInstance().setDisplayPowerState(m_disp_id, mode);
 
    HWCDispatcher::getInstance().setPowerMode(m_disp_id, mode);
 
    DisplayManager::getInstance().setPowerMode(m_disp_id, mode);
    ......
 
}
    这一块的代码都会被编译到hwcomposer.<platform>.so库中,之前部分平台此部分代码不开源,当前新的一些平台,这部分的代码已经开源,所以可以看到上面部分的代码,下面才是真正完成setPowerMode工作的函数体:

void DispDevice::setPowerMode(int dpy,int mode)
{
    if (HWCMediator::getInstance().m_features.control_fb)
    {
        HWC_LOGD("DispDevice::setPowerMode() dpy:%d mode:%d", dpy, mode);
        char filename[32] = {0};
        //打开/dev/graphics/fb%d的节点进行ioctl的操作
        snprintf(filename, sizeof(filename), "/dev/graphics/fb%d", dpy);
        int fb_fd = open(filename, O_RDWR);
        if (fb_fd <= 0)
        {
            HWC_LOGE("Failed to open fb%d device: %s", dpy, strerror(errno));
            return;
        }
 
        switch (mode)
        {
            case HWC_POWER_MODE_OFF://灭屏
            {
                err = WDT_IOCTL(fb_fd, FBIOBLANK, FB_BLANK_POWERDOWN);
                break;
            }
            case HWC_POWER_MODE_NORMAL://亮屏
            {
                err = WDT_IOCTL(fb_fd, FBIOBLANK, FB_BLANK_UNBLANK);
                m_color_transform_info[dpy].resend_color_transform = true;
                break;
            }
            case HWC_POWER_MODE_DOZE:
            case HWC_POWER_MODE_DOZE_SUSPEND:
            {
                ......
            }
        protectedClose(fb_fd);
    }
}
三、kernel resume work:

    上面setPowerMode会直接通过fb0-x的节点调用到kernel的代码中:

//kernel-3.18/drivers/misc/mediatek/video/common/mtkfb.c
static struct fb_ops mtkfb_ops = {
    .owner = THIS_MODULE,
    .fb_ioctl = mtkfb_ioctl,
#ifdef CONFIG_COMPAT
    .fb_compat_ioctl = mtkfb_compat_ioctl,
#endif
#if defined(CONFIG_PM_AUTOSLEEP)
    .fb_blank = mtkfb_blank,
#endif
 
}
 
static int mtkfb_blank(int blank_mode, struct fb_info *info)
{
    enum mtkfb_power_mode prev_pm = primary_display_get_power_mode();
 
    switch (blank_mode) {
    case FB_BLANK_UNBLANK:
    case FB_BLANK_NORMAL:
        DISPDBG("mtkfb_blank mtkfb_late_resume\n");
        if (bypass_blank) {
            DISPERR("FB_BLANK_UNBLANK bypass_blank %d\n", bypass_blank);
            break;
        }
 
        primary_display_set_power_mode(FB_RESUME);
        mtkfb_late_resume();//正式进入到late_resume相关屏的操作
 
        debug_print_power_mode_check(prev_pm, FB_RESUME);
 
        if (!lcd_fps)
            msleep(30);
        else
            msleep(2 * 100000 / lcd_fps);    /* Delay 2 frames. */
        break;

--------------------- 

原文:https://blog.csdn.net/frank_zyp/article/details/79291164 
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值