MTK 平臺充電開機

MTK平臺修改Bootloader源代碼,讓Android設備一通電就自動開機

爲什麼要一通電就自動開機
總所周知,一臺Android設備,默認情況下,插上USB供電,都是顯示正在充電的電池圖標,按住開機鍵才能開機。如果你手裏只有一臺手機,按住開機鍵去開機很容易,如果你手裏有1000臺手機呢?通常見於微信推廣、微信站街、手遊工作室、刷單工作室等,往往擁有數百臺數千臺手機,使用羣控和腳本批量控制這些手機做一些interesting的事情。這些工作室往往使用定製的手機,手裏握着全套Android源代碼,這樣可以方便的做很多root都做不到的事情。那麼一個工作室如果有數百臺手機,如果斷電了,需要開機,或者需要重啓,都是很痛苦的事情,如果能讓這些Android手機一通電就自動開機,豈不是方便極了。那麼在有全套Android源代碼的情況下,如何分析代碼並修改去實現這一功能呢?

思路
其實一通電就開機這個功能,和Android沒什麼關係,分析的時候,也不要再去看Android源代碼了。首先了解一下Android設備的啓動流程:


實際上通電以後,屏幕上出現電池充電的圖標,就一直停留在Bootloader這個步驟了,電池充電和屏幕顯示充電的動畫都是在這裏面實現的,這個時候連Linux內核都還沒加載,更不用說Android了。
所以要實現這個功能,首先鎖定代碼位置,那就是Android的Bootloader,通常是LK,代碼位於bootable/bootloader/lk/。鎖定了範圍,只需要分析這裏面的代碼就行了。LK的代碼量並不多,只有幾千行,藉助開發板使用串口打LOG調試,觀察LOG輸出內容,並以此爲關鍵詞在代碼中搜索,定位LOG對應的代碼,相信你很快就能找到方法。這裏有網上找到的兩篇文章,以MTK平臺爲例,分析了按下開機鍵後,由Preloader -> LK -> Kernel 的過程,結合文章中的時序圖去閱讀LK代碼,會得到事半功倍的效果,相信你很快就能分析出來,充電的時候,代碼執行到那裏,按住開機鍵的時候,代碼執行到了哪裏。
深入MTK平臺bootloader啓動之【 Pre-loader -> Lk】分析筆記
深入MTK平臺bootloader啓動之【 lk -> kernel】分析筆記

MTK平臺改法
有了思路以後,使用開發板串口打LOG,通電進入到充電界面後,觀察串口輸出的日誌,用日誌內容中的關鍵詞去LK代碼中搜索,進一步定位關鍵代碼位置,通過閱讀代碼,bootable\bootloader\lk\platform\mt6582\boot_mode.c 中的 void boot_mode_select(void) 函數引起了我的注意,看函數名,很可能是通電以後,選擇如何boot,是開機呢,還是充電呢,還是進入recovery。

void boot_mode_select(void)
{

    int factory_forbidden = 0;
  //  int forbid_mode;
/*We put conditions here to filer some cases that can not do key detection*/
    extern int kedump_mini(void) __attribute__((weak));
    if (kedump_mini) 
    {
    if (kedump_mini()) 
    {
        mrdump_check();
        return;
    }
    }
    if (meta_detection())
    {
      return;
    }

    mrdump_check();

#if defined (HAVE_LK_TEXT_MENU)
/*Check RTC to know if system want to reboot to Fastboot*/
    if(Check_RTC_PDN1_bit13())
    {
      printf("[FASTBOOT] reboot to boot loader\n");
      g_boot_mode = FASTBOOT;
      Set_Clr_RTC_PDN1_bit13(false);
          return;
    }
    /*If forbidden mode is factory, cacel the factory key detection*/ 
    if(g_boot_arg->sec_limit.magic_num == 0x4C4C4C4C)
    {
        if(g_boot_arg->sec_limit.forbid_mode == F_FACTORY_MODE)
        {
            //Forbid to enter factory mode
            printf("%s Forbidden\n",MODULE_NAME);
            factory_forbidden=1;
        }
    }
  //  forbid_mode = g_boot_arg->boot_mode &= 0x000000FF;
    /*If boot reason is power key + volumn down, then
     disable factory mode dectection*/
    if(mtk_detect_pmic_just_rst())
    {
          factory_forbidden=1;
    }
    /*Check RTC to know if system want to reboot to Recovery*/
    if(Check_RTC_Recovery_Mode())
    {
          g_boot_mode = RECOVERY_BOOT;
          return;
    }
    /*If MISC Write has not completed  in recovery mode 
     before system reboot, go to recovery mode to
    finish remain tasks*/     
    if(unshield_recovery_detection())
    {
        return;
    }
    ulong begin = get_timer(0);

/*we put key dectection here to detect key which is pressed*/
    printf("eng build\n");
    printf("MT65XX_FACTORY_KEY 0x%x\n",MT65XX_FACTORY_KEY);
    printf("MT65XX_BOOT_MENU_KEY 0x%x\n",MT65XX_BOOT_MENU_KEY);
    printf("MT65XX_RECOVERY_KEY 0x%x\n",MT65XX_RECOVERY_KEY);
    while(get_timer(begin)<50)
    {


        if(!factory_forbidden){
                   if(mtk_detect_key(MT65XX_FACTORY_KEY))
                   {
                          printf("%s Detect key\n",MODULE_NAME);
                          printf("%s Enable factory mode\n",MODULE_NAME);
                          g_boot_mode = FACTORY_BOOT;
                          //video_printf("%s : detect factory mode !\n",MODULE_NAME);
                          return;
                   }
        }

        if(mtk_detect_key(MT65XX_BOOT_MENU_KEY))
         {
                           printf("\n%s Check  boot menu\n",MODULE_NAME);
                           printf("%s Wait 50ms for special keys\n",MODULE_NAME);
                           mtk_wdt_disable();
                           boot_mode_menu_select();
                           mtk_wdt_init();
                           return;  
         }
#ifdef MT65XX_RECOVERY_KEY
            if(mtk_detect_key(MT65XX_RECOVERY_KEY))
           {
                printf("%s Detect cal key\n",MODULE_NAME);
                printf("%s Enable recovery mode\n",MODULE_NAME);
                g_boot_mode = RECOVERY_BOOT;
                //video_printf("%s : detect recovery mode !\n",MODULE_NAME);
                return;
            }
#endif                   
    }
#else

/*We put conditions here to filer some cases that can not do key detection*/

/*Check RTC to know if system want to reboot to Fastboot*/
#ifdef MTK_FASTBOOT_SUPPORT
  if(Check_RTC_PDN1_bit13())
  {
    dprintf(INFO,"[FASTBOOT] reboot to boot loader\n");
    g_boot_mode = FASTBOOT;
    Set_Clr_RTC_PDN1_bit13(false);
         return ;
  }
#endif

   /*If forbidden mode is factory, cacel the factory key detection*/
    if(g_boot_arg->sec_limit.magic_num == 0x4C4C4C4C)
    {
        if(g_boot_arg->sec_limit.forbid_mode == F_FACTORY_MODE)
        {
            //Forbid to enter factory mode
            printf("%s Forbidden\n",MODULE_NAME);
            factory_forbidden=1;
        }
    }
//    forbid_mode = g_boot_arg->boot_mode &= 0x000000FF;
    /*If boot reason is power key + volumn down, then 
     disable factory mode dectection*/
    if(mtk_detect_pmic_just_rst())
    {
          factory_forbidden=1;
    }
    /*Check RTC to know if system want to reboot to Recovery*/
    if(Check_RTC_Recovery_Mode())
    {
          g_boot_mode = RECOVERY_BOOT;
          return ;
    }
    /*If MISC Write has not completed  in recovery mode 
     and interrupted by system reboot, go to recovery mode to 
    finish remain tasks*/
    if(unshield_recovery_detection())
    {
        return ;
    }
    ulong begin = get_timer(0);
/*we put key dectection here to detect key which is pressed*/
while(get_timer(begin)<50){
#ifdef MTK_FASTBOOT_SUPPORT
    if(mtk_detect_key(MT_CAMERA_KEY))
    {
          dprintf(INFO,"[FASTBOOT]Key Detect\n");
          g_boot_mode = FASTBOOT;
          return ;
    }
#endif
    if(!factory_forbidden){
                if(mtk_detect_key(MT65XX_FACTORY_KEY))
                {
                       printf("%s Detect key\n",MODULE_NAME);
                       printf("%s Enable factory mode\n",MODULE_NAME);
                       g_boot_mode = FACTORY_BOOT;
                       //video_printf("%s : detect factory mode !\n",MODULE_NAME);
                       return ;
                }
     }
#ifdef MT65XX_RECOVERY_KEY
        if(mtk_detect_key(MT65XX_RECOVERY_KEY))
        {
             printf("%s Detect cal key\n",MODULE_NAME);
             printf("%s Enable recovery mode\n",MODULE_NAME);
             g_boot_mode = RECOVERY_BOOT;
             //video_printf("%s : detect recovery mode !\n",MODULE_NAME);
             return ;
         }
#endif
}

#endif

#ifdef MTK_KERNEL_POWER_OFF_CHARGING
    if(kernel_power_off_charging_detection())
    {
        printf(" < Kernel Power Off Charging Detection Ok> \n");
        return;
    }
    else
    {
        printf("< Kernel Enter Normal Boot > \n");
    }
#endif
}


boot_mode_select函數中的g_boot_mode變量尤爲重要,不同的賦值決定着它boot什麼。在boot_mode_select函數中可以看到所有的分支,對boot_mode_select的賦值,要麼是NORMAL_BOOT要麼是FASTBOOT要麼是RECOVERY_BOOT,似乎都和充電界面沒什麼關係啊。但是注意函數的尾部:

#ifdef MTK_KERNEL_POWER_OFF_CHARGING
    if(kernel_power_off_charging_detection())
    {
        printf(" < Kernel Power Off Charging Detection Ok> \n");
        return;
    }
    else
    {
        printf("< Kernel Enter Normal Boot > \n");
    }
#endif

看到了power_off_charging,似乎和關機狀態下的充電有關係,再看兩個分支的printf,一個分支和充電相關,一個分支是Kernel Enter Normal Boot正常開機啓動。馬上搜索kernel_power_off_charging_detection的定義:
find -name “*.c” | xargs grep “kernel_power_off_charging_detection”
在bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c中找到了這個函數,看文件名,顯然是和關機充電相關的代碼:

BOOL kernel_power_off_charging_detection(void)
{
    int off_mode_status = 1;
    /* */
    if(is_force_boot()) {
        upmu_set_rg_chrind_on(0);
        printf("[%s] Turn off HW Led\n", __func__);
        return FALSE;
    }

    off_mode_status = get_off_mode_charge_status();
    printf("[%s] off_mode_status %d\n", __func__, off_mode_status);
    if(upmu_is_chr_det() == KAL_TRUE) {
        if (off_mode_status) {
            g_boot_mode = KERNEL_POWER_OFF_CHARGING_BOOT;
        } else {
            g_boot_mode = NORMAL_BOOT;
            upmu_set_rg_chrind_on(0);
            return FALSE;
        }
        return TRUE;
    }
    else {
        /* power off */
        #ifndef NO_POWER_OFF
        printf("[kernel_power_off_charging_detection] power off\n");
        mt6575_power_off();        
        #endif
        return FALSE;   
    }
    /* */
}

可以看到這裏對g_boot_mode有兩種賦值,一種是關機狀態下充電,一種是正常開機。那麼一個改動思路就是將:
g_boot_mode = KERNEL_POWER_OFF_CHARGING_BOOT;
改成
g_boot_mode = NORMAL_BOOT;
這樣原本要進行關機充電的,改成了正常開機啓動。

還有一種找關鍵代碼的思路是,找到了g_boot_mode這個變量後,在LK代碼目錄中搜索,看看哪裏對它進行了賦值:
find -name “*.c” | xargs grep “g_boot_mode = ”
可以看到,只有bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c 文件中對g_boot_mode賦值爲KERNEL_POWER_OFF_CHARGING_BOOT,其它地方都是賦值爲NORMAL_BOOT、FASTBOOT、RECOVERY_BOOT、ALARM_BOOT等,明顯和充電無關。這樣也能快速定位到關鍵代碼,究竟是哪裏,讓它進入了關機充電那個界面。

此時還沒有結束,我又注意到了這個宏MTK_KERNEL_POWER_OFF_CHARGING:

#ifdef MTK_KERNEL_POWER_OFF_CHARGING
    if(kernel_power_off_charging_detection())
    {
        printf(" < Kernel Power Off Charging Detection Ok> \n");
        return;
    }
    else
    {
        printf("< Kernel Enter Normal Boot > \n");
    }
#endif

在bootable\bootloader\lk\platform\mt6582\boot_mode.c的 boot_mode_select 函數中,如果代碼運行到這裏,又沒定義MTK_KERNEL_POWER_OFF_CHARGING這個宏,宏內這段代碼不會執行,那麼此時g_boot_mode的賦值是什麼?當然是初始值NORMAL_BOOT,當然這只是猜想,萬一哪個函數裏改變了它的值呢,接下來在這個位置插入打LOG的代碼把g_boot_mode的值輸出來看看,果然是NORMAL_BOOT。那麼嘗試取消定義MTK_KERNEL_POWER_OFF_CHARGING這個宏,是不是就能實現直接開機的效果呢?經過測試,果然如此。

這個宏的意思是是否允許在關機狀態下充電,可以理解爲,如果不允許,那麼插電後,設備會開機再充電。
當然,宏的改法是MTK平臺特有的,其它平臺可以參考代碼的改法,雖然不同平臺的LK代碼有差異,但總體流程還是不變的。

那麼,如何取消這個宏的定義呢:
通常這個宏位於ProjectConfig.mk中,具體路徑各不相同,如果找不到的話可以find -name “ProjectConfig.mk”搜索,另外有的同學改了以後還是無效,那可能不止這一處的mk文件定義了MTK_KERNEL_POWER_OFF_CHARGING哦,最簡單的辦法是直接全局搜索:
find -name “*.mk” | xargs grep “MTK_KERNEL_POWER_OFF_CHARGING = yes”
然後全部改爲MTK_KERNEL_POWER_OFF_CHARGING = no

改完以後重新編譯LK
mmm bootable/bootloader/lk:clean-lk
mmm bootable/bootloader/lk:lk
然後make snod 並 Download
USB插入,通電

Perfect!

總結
① 對於MTK平臺,關鍵在於MTK_KERNEL_POWER_OFF_CHARGING這個宏,搜索源代碼目錄下的所有mk文件:
find -name “*.mk” | xargs grep “MTK_KERNEL_POWER_OFF_CHARGING = yes”
將其替換爲爲MTK_KERNEL_POWER_OFF_CHARGING = no即可

② 如果是MTK平臺,但是沒有MTK_KERNEL_POWER_OFF_CHARGING這個宏,嘗試找到bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c文件
你的源碼不一定是這個路徑,嘗試搜索mt_kernel_power_off_charging.c
find -name “mt_kernel_power_off_charging.c”
裏面有個kernel_power_off_charging_detection(void)函數,函數內對g_boot_mode的賦值,通通改爲g_boot_mode = NORMAL_BOOT;

③ 對於高通等其它平臺,如果沒有方法②中的函數,需要花點功夫了,首先定位bootloader的lk部分代碼,然後搜索這些和啓動模式相關的關鍵詞:
g_boot_mode NORMAL_BOOT FASTBOOT RECOVERY_BOOT
搜索哪些地方對g_boot_mode賦值了:
find -name “*.c” | xargs grep “g_boot_mode = ”
着重去分析這些地方,配合打LOG調試,相信你很快就能搞定

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章