【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

  • 一、加载 UEFI 默认应用程序
    • 1.1 LaunchDefaultBDSApps()
    • 1.1 LaunchAppFromGuidedFv()
  • 二、QcomChargerApp应用程序初始化
    • 2.1 入口函数 QcomChargerApp_Entry()
    • 2.2 充电初始化 QcomChargerApp_Initialize()
    • 2.3 循环监控充电状态 QcomChargerApp_MonitorCharging()
  • 三、充电驱动各函数分析
    • 3.1 充电初始化 ChargerPlatform_Init()
    • 3.2 更改充电状态 pQcomChargerProtocol->TakeAction()
      • 3.2.1直接关机 CRITICAL、SHUTDOWN
      • 3.2.2 开始/停止 充电 START_CHARGING、STOP_CHARGING
      • 3.2.3 继续充电 CONTINUE
    • 3.3 获取当前充电状态 pQcomChargerProtocol->GetChargingAction()
    • 3.4 显示充电图标 pQcomChargerProtocol->DisplayImage()
      • 3.4.1 获取图片路径 ChargerLibEvent_AsciiStrNDup()
      • 3.4.2 绘制图片DrawBmpFile()

系列文章:

  1. 《【Android SDM660开机流程】- UEFI XBL 代码流程分析》
  2. 《【Android SDM660源码分析】- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序》
  3. 《【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析》
  4. 《【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程》
  5. 《【Android SDM660源码分析】- 04 - UEFI ABL LinuxLoader 代码分析》

一、加载 UEFI 默认应用程序

1.1 LaunchDefaultBDSApps()

在高通代码中,QcomChargerApp是作为默认应用程序配置在uefiplat.cfg中,

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\LA\uefiplat.cfg## Default app to boot in platform BDS init
DefaultChargerApp = "QcomChargerApp"
DefaultBDSBootApp = "LinuxLoader"

系统开机过程中,初始化 Bsd 时,会调用LaunchDefaultBDSApps()函数,在该函数中,会实现对上述两个默认app 的调用。

  1. 解析 uefiplat.cfg 中的 DefaultChargerApp,字符串保存在 DefaultApp 数组中。
  2. 加载 QcomCharger App 应用程序,传参gMainFvGuid 和 “DefaultChargerApp”
  3. 解析 uefiplat.cfg 中的 DefaultBDSBootApp,字符串保存在 DefaultApp 数组中
  4. 加载 DefaultBDSBootApp 应用程序,加载后不再返回
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Library\PlatformBdsLib\PlatformBdsLib.c
STATIC EFI_STATUS EFIAPI LaunchDefaultBDSApps (VOID)
{// 1. 解析 uefiplat.cfg中的DefaultChargerApp,字符串保存在DefaultApp数组中。CHAR8 DefaultApp[DEF_APP_STR_LEN];Status = GetConfigString ("DefaultChargerApp", DefaultApp, &Size);// 2. 加载QcomChargerApp 应用程序,传参gMainFvGuid 和 “DefaultChargerApp”Status = LaunchAppFromGuidedFv(&gMainFvGuid, DefaultApp, NULL);// 3. 解析 uefiplat.cfg中的DefaultBDSBootApp,字符串保存在DefaultApp数组中。Size = DEF_APP_STR_LEN;Status = GetConfigString ("DefaultBDSBootApp", DefaultApp, &Size);DisplayPOSTTime ();      // 打印当前耗费的时间// 4. 加载DefaultBDSBootApp 应用程序,加载后不再返回LaunchAppFromGuidedFv(&gEfiAblFvNameGuid, DefaultApp, NULL);//If we return from above function, considered a failureConfirmShutdownOnFailure();
}

1.1 LaunchAppFromGuidedFv()

接下来,我们来看下 LaunchAppFromGuidedFv() 函数做了什么动作。

EFI_STATUS LaunchAppFromGuidedFv(EFI_GUID *FvGuid, CHAR8 *AppName, CHAR8 *Args OPTIONAL)
{// 1. 将传入的AppName="QcomChargerApp" 从Ascii编码转换为 Unicode编码StrAppNameStr = AllocatePool(2 * AsciiStrSize(AppName));AsciiStrToUnicodeStr(AppName, AppNameStr);// 2. 匹配字符串Status = GetAppFileDevPath(FvGuid,AppNameStr,&FilePath);============>Status = ConnectByProtocol(&gEfiFirmwareVolume2ProtocolGuid);Status = gBS->LocateHandleBuffer (ByProtocol,&gEfiFirmwareVolume2ProtocolGuid,NULL,&Fv2HandleCount, &Fv2Handles);// 2.1 循环查找 与 gMainFvGuid 匹配的固件fv地址。//Search through handles looking for matching guided FVfor (i = 0; i < Fv2HandleCount; i++){Status = gBS->HandleProtocol(Fv2Handles[i],&gEfiDevicePathProtocolGuid,(VOID **) &FvDevicePath);}// 2.2 找到后,直接返回&Fv2Handles[i]->PathName*FilePath = FileDevicePath(Fv2Handles[i], AppName);<============// 3. 从 FilePath 指向的路径加载镜像,镜像的句柄保存在ImageHandle中Status = gBS->LoadImage (TRUE, gImageHandle, FilePath, NULL, 0, &ImageHandle);// 4. ImageHandler指向的镜像,也就是本文中的DefaultChargerAppStatus = gBS->StartImage(ImageHandle, NULL, NULL);return EFI_SUCCESS;
}

二、QcomChargerApp应用程序初始化

2.1 入口函数 QcomChargerApp_Entry()

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Application\QcomChargerApp\QcomChargerApp.inf
[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = QcomChargerAppFILE_GUID                      = EEE9C2B1-16CA-4f34-87EA-2E6D1E160CC4MODULE_TYPE                    = UEFI_APPLICATIONVERSION_STRING                 = 1.0ENTRY_POINT                    = QcomChargerApp_Entry

QcomChargerApp.inf中可知,其入口函数为 QcomChargerApp_Entry(),我们跳转进入分析下:

  1. 获得当前的平台类型,如EFI_PLATFORMINFO_TYPE_MTP=0x08
  2. 如果当前平台是 EFI_PLATFORMINFO_TYPE_CDPEFI_PLATFORMINFO_TYPE_RUMI,则不需要运行充电应用程序
  3. 获得充电驱动的结构体函数,结构体指针保存在pQcomChargerProtocol中。
  4. 获得当前的电池状态,如果不等于EFI_QCOM_CHARGER_ACTION_DEBUG_BOARD_GOOD_TO_BOOT
  5. 判断是否需要加载充电电池曲线数参数FG battery profile
  6. 如果当前电池状态不允许正常开机,则调用QcomChargerApp_Initialize() 初始化PMIC充电,且在QcomChargerApp_MonitorCharging()中开始循环监控充电状态
  7. 退出充电模式
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Application\QcomChargerApp\QcomChargerApp.cEFI_STATUS QcomChargerApp_Entry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{// 1. 获得当前的平台类型,如EFI_PLATFORMINFO_TYPE_MTP=0x08等Status |= GetPlatformType(&PlatformType);// 2. 如果当前平台是 EFI_PLATFORMINFO_TYPE_CDP  或 EFI_PLATFORMINFO_TYPE_RUMI,则不需要运行充电应用程序if((EFI_PLATFORMINFO_TYPE_CDP == PlatformType) || (EFI_PLATFORMINFO_TYPE_RUMI == PlatformType)){    /*if platform is CDP/RUMI , don't need to run the app. */CHARGERAPP_FILE_UART_DEBUG(( EFI_D_WARN, "QcomChargerApp:: %a CDP/RUMI (%d) Platform detected. Exiting app. \r\n", __FUNCTION__, PlatformType));return EFI_SUCCESS;}// 3. 获得充电驱动的结构体函数,结构体指针保存在pQcomChargerProtocol中。Status = gBS->LocateProtocol( &gQcomChargerProtocolGuid, NULL, (VOID **)&pQcomChargerProtocol );// 4. 获得当前的电池状态,如果不等于EFI_QCOM_CHARGER_ACTION_DEBUG_BOARD_GOOD_TO_BOOT/*Get charging Action to be taken */Status = pQcomChargerProtocol->GetChargingAction(&ChargingAction, &ChargerActionInfo);// 5. 判断是否需要加载充电电池曲线数参数FG battery profile/* Handle if FG battery profile needs to be loaded */if(EFI_QCOM_CHARGER_PLATFORM_ACTION_PROFILE_LOAD == ChargingAction){if(ChargerActionInfo.ProfState == EFI_QCOM_CHARGER_PROFILE_LOAD){Status |= QcomChargerApp_DisplaySignOfLifeOnVBat(&ChargerActionInfo);}/* Load Battery Profile */Status |= pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);/*Get charging Action again to be taken to proceed further for charging if needed */Status = pQcomChargerProtocol->GetChargingAction(&ChargingAction, &ChargerActionInfo);}// 6. 如果当前电池状态不允许正常开机,则调用QcomChargerApp_Initialize() 显示充电图标及启动一个5s的定时器,且在QcomChargerApp_MonitorCharging()中开始循环监控充电状态/* Take Action First skip if debug board i.e. boot to HLOS */if(EFI_QCOM_CHARGER_ACTION_DEBUG_BOARD_GOOD_TO_BOOT != ChargingAction){Status |= pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);/* DO charger App Actions */switch(ChargingAction){case EFI_QCOM_CHARGER_ACTION_START_CHARGING:case EFI_QCOM_CHARGER_ACTION_NO_CHARGE_WAIT:case EFI_QCOM_CHARGER_ACTION_CONTINUE:/*Initializes and displays the battery symbol*/Status |= QcomChargerApp_Initialize(ChargingAction);/* if initialization success then start charging upto target SoC*/Status |= QcomChargerApp_MonitorCharging();Status |= QcomChargerApp_PostProcessing();break;}}// 7. 退出充电模式/* After post processing call to exit module */Status |= QcomChargerApp_DeInitialize();CHARGERAPP_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a Exiting \r\n", __FUNCTION__));return Status;
}

2.2 充电初始化 QcomChargerApp_Initialize()

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Application\QcomChargerApp\QcomChargerApp.c
EFI_STATUS QcomChargerApp_Initialize( EFI_QCOM_CHARGER_ACTION_TYPE ChargingAction )
{if(!pQcomChargerProtocol)Status = gBS->LocateProtocol( &gQcomChargerProtocolGuid, NULL, (VOID **)&pQcomChargerProtocol );/*Start required timers */Status |= QcomChargerAppEvent_DisplayTimerEvent(TRUE, TRUE);CHARGERAPP_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a Status = %r \r\n", __FUNCTION__, Status));return Status;
}

QcomChargerApp_Initialize() 中主要动作就是

  1. 显示充电图片
  2. 创建一个定时事件,定时时间为5s,如果时间到达则会发送EVT_NOTIFY_SIGNAL事件
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Application\QcomChargerApp\QcomChargerAppEventHandler.c
EFI_STATUS QcomChargerAppEvent_DisplayTimerEvent(BOOLEAN bDispOffTimer, BOOLEAN AnimTimer)
{switch (bDispOffTimer){case TRUE:/* Create Animation timer */if(NULL == EventAnimImg ){/* To Clear screen from snapdragon logo */if(NULL == pQcomChargerProtocol)Status = gBS->LocateProtocol( &gQcomChargerProtocolGuid, NULL, (VOID **)&pQcomChargerProtocol );// 1. 显示充电图片pQcomChargerProtocol->DisplayImage(EFI_QCOM_CHARGER_DISP_IMAGE_LOWBATTERYCHARGING, TRUE);// 2. 创建一个定时事件,定时时间为5s,如果时间到达则会发送EVT_NOTIFY_SIGNAL事件Status |= gBS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, QcomChargerAppEvent_AnimImgTimer, NULL, /*GUID Display guys provide*/ &EventAnimImg);}if (TRUE == AnimTimer){Status |= gBS->SetTimer(EventAnimImg, TimerPeriodic, CHGAPP_ANIM_TIMER_DURATION_IN_US);  //#define CHGAPP_ANIM_TIMER_DURATION_IN_US  5000000 /*500 milli sec*/DisplayOffCount = 0; /* Reset DisplyOff Count */}break;}return Status;
}

2.3 循环监控充电状态 QcomChargerApp_MonitorCharging()

  1. 关闭 看门狗
  2. 查询当前的充电状态
  3. 配置超时时间,如果配置了充电LED灯,超时时间为500ms,否则为3s
  4. 循环获取当前的充电状态
  5. 根据当前状态判断是否要开机、关机、继续充电
  6. 开启10分钟的看门狗定时器
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Application\QcomChargerApp\QcomChargerApp.c
/*
ChgAppMonitorCharging(): This is the charging loop that will keep the chargerApp running, it will also call ChargerPlatform_Periodic function to get the error status,
get charging/gauging status, control charging and determine when to exit charging loop.
*/
EFI_STATUS QcomChargerApp_MonitorCharging( VOID )
{CHARGERAPP_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a \r\n", __FUNCTION__));if(!pQcomChargerProtocol)Status = gBS->LocateProtocol( &gQcomChargerProtocolGuid, NULL, (VOID **)&pQcomChargerProtocol );// 1. 关闭 看门狗/* Disable crash dump watchdog only when charger app starts charging */gBS->SetWatchdogTimer(0, 0, 0, NULL);// 2. 查询当前的充电状态/* Query charging action */Status = pQcomChargerProtocol->GetChargingAction(&ChargingAction, &ChargerActionInfo);CHARGERAPP_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a ChargingAction = %d Status = %r LedConfigType = %d \r\n", __FUNCTION__, ChargingAction, Status, ChargerActionInfo.LedConfigType));CHARGERAPP_FILE_DEBUG((EFI_D_WARN, "RebootCount,TimeStamp,StateOfCharge,Voltage,ChargeCurrent,Temp \r\n"));DEBUG((EFI_D_WARN, "QcomChargerApp::%a TimeStamp,StateOfCharge,Voltage,ChargeCurrent,Temp \r\n",__FUNCTION__));// 3. 配置超时时间,如果配置了充电LED灯,超时时间为500ms,否则为3s/* Calculate Time to know if IDLE wait have reached to print logs and get next action */if(ChargerActionInfo.LedConfigType == EFI_QCOM_CHARGER_CHARGING_LED_TOGGLE){/* wait for IDLE duration to get status and print status info */Timeoutms = QCOM_CHARGER_TOGGLE_LED_WAIT_DURATION_IN_MS;       // 500ms}else{/* Normal wait for 3s*/Timeoutms      = QCOM_CHARGER_IDLE_WAIT_DURATION_IN_MS;           // 3s}do{// 4. 循环获取当前的充电状态/* Get charging action to be taken */Status = pQcomChargerProtocol->GetChargingAction(&ChargingAction, &ChargerActionInfo);CHARGERAPP_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a ChargingAction = %d Status = %r LedConfigType = %d \r\n", __FUNCTION__, ChargingAction, Status, ChargerActionInfo.LedConfigType));// 5. 根据当前状态判断是否要开机、关机、继续充电switch(ChargingAction){case EFI_QCOM_CHARGER_ACTION_GOOD_TO_BOOT:/*TBD Handle Charging actions with Switch statement */{Status = pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);QcomChargerAppEvent_ExitLPM();QcomChargerAppEvent_DisplayTimerEvent(FALSE, FALSE);ExitChargingLoop = TRUE;}break;case EFI_QCOM_CHARGER_ACTION_SHUTDOWN:{/*Exit LPM and Display image before shutting down*/QcomChargerAppEvent_ExitLPM();QcomChargerAppEvent_DisplayTimerEvent(FALSE, FALSE);}Status = pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);/* Control does not return here as in case to shutdown action */ExitChargingLoop = TRUE;break;case EFI_QCOM_CHARGER_ACTION_NO_CHARGE_WAIT:{/* Take Action to make sure charging is disabled */Status = pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);CHARGERAPP_FILE_UART_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a No Charge Wait ChrgAct = %d wait %d ms \r\n", __FUNCTION__, ChargingAction, QCOM_CHARGER_IDLE_WAIT_DURATION_IN_MS));WaitForTimeout (QCOM_CHARGER_IDLE_WAIT_DURATION_IN_MS, QCOM_CHARGER_TIMEOUT_WAIT_FOR_KEY, &UserKey);/*Check if WaitForTimeout returned due to key press */if((UserKey.UnicodeChar != 0x00) || (UserKey.ScanCode != 0x00)){DEBUG((EFI_D_WARN, "QcomChargerApp:: %a Keypress detected \r\n", __FUNCTION__));QcomChargerAppEvent_KeyPressHandler(QCOMCHARGERAPP_EVENT_ANIMATION__LOW_BATTERY_NO_ANIM);}}break;case EFI_QCOM_CHARGER_ACTION_CRITICAL:{/*Exit LPM and Display image before shutting down*/QcomChargerAppEvent_ExitLPM(  );Status = pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);/* Control does not return here as in case to shutdown action */ExitChargingLoop = TRUE;}break;case EFI_QCOM_CHARGER_ACTION_SHUTDOWN_USB_DC_PON_DISABLED:{/*Exit LPM and Display image before shutting down*/QcomChargerAppEvent_ExitLPM();CHARGERAPP_FILE_UART_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a Shutdown with PON disabled \r\n", __FUNCTION__));Status = pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);/* Control does not return here as in case to shutdown action */ExitChargingLoop = TRUE;}break;case EFI_QCOM_CHARGER_ACTION_CONTINUE:default:/* Take Action */Status |= pQcomChargerProtocol->TakeAction(ChargingAction, &ChargerActionInfo);QcomChargerApp_LogParam(&ChargerActionInfo);CHARGERAPP_FILE_UART_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a Waiting for %d ms \r\n", __FUNCTION__, Timeoutms));/* Now wait for set Timeout */WaitForTimeout (Timeoutms, QCOM_CHARGER_TIMEOUT_WAIT_FOR_KEY, &UserKey);/*Check if WaitForTimeout returned due to key press */if((UserKey.UnicodeChar != 0x00) || (UserKey.ScanCode != 0x00)){DEBUG((EFI_D_WARN, "QcomChargerApp:: %a Keypress detected \r\n", __FUNCTION__));QcomChargerAppEvent_KeyPressHandler(QCOMCHARGERAPP_EVENT_ANIMATION__CHARGING_ANIM);}break;}}while (FALSE == ExitChargingLoop);// 6. 开启10分钟的看门狗定时器/* Start 10min watchdog timer */gBS->SetWatchdogTimer(10 * 60, 0, 0, NULL);CHARGERAPP_DEBUG((EFI_D_WARN, "QcomChargerApp:: %a Exiting ChargingAction = %d \r\n", __FUNCTION__, ChargingAction));return Status;
}

三、充电驱动各函数分析

通过前面代码:gBS->LocateProtocol( &gQcomChargerProtocolGuid, NULL, (VOID **)&pQcomChargerProtocol );
充电驱动是和gQcomChargerProtocolGuid绑定在一起,接下来,我们看下它的代码。

3.1 充电初始化 ChargerPlatform_Init()

gQcomChargerProtocolGuid 对应的代码位于amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe\QcomCharger.c中。
amss\BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\LA\Sdm660Pkg.fdf 中,
可知当前SDM660平台调用的是QcomPkg/Drivers/QcomChargerDxe/QcomChargerDxeLA.inf 文件,
在fdf 文件中还定义了充电图标相当资源的路径。

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\LA\Sdm660Pkg.fdf# Charger Driver#INF QcomPkg/Drivers/QcomChargerDxe/QcomChargerDxeLA.inf# Applications#QCOM Charger appINF QcomPkg/Application/QcomChargerApp/QcomChargerApp.infFILE FREEFORM = a91d838e-a5fa-4138-825d-455e2303079e {SECTION UI = "BDS_Menu.cfg"SECTION RAW = QcomPkg/Library/PlatformBdsLib/LA/bds_menu.cfg}FILE FREEFORM = a91d838e-a5fa-4138-825d-455e23030794 {SECTION UI = "logo1.bmp"SECTION RAW = QcomPkg/Logo/LA/logo1.bmp}FILE FREEFORM = 3E5584ED-05D4-4267-9048-0D47F76F4248 {SECTION UI = "battery_symbol_Soc10.bmp"SECTION RAW = QcomPkg/Drivers/QcomChargerDxe/battery_symbol_Soc10.bmp}FILE FREEFORM = 4753E815-DDD8-402d-BF69-9B8C4EB7573E {SECTION UI = "battery_symbol_NoBattery.bmp"SECTION RAW = QcomPkg/Drivers/QcomChargerDxe/battery_symbol_NoBattery.bmp}FILE FREEFORM = 03DED53E-BECD-428f-9F79-5ABA64C58445 {SECTION UI = "battery_symbol_Nocharger.bmp"SECTION RAW = QcomPkg/Drivers/QcomChargerDxe/battery_symbol_Nocharger.bmp}FILE FREEFORM = 8b86cd38-c772-4fcf-85aa-345b2b3c1ab4 {SECTION UI = "battery_symbol_LowBatteryCharging.bmp"SECTION RAW = QcomPkg/Drivers/QcomChargerDxe/battery_symbol_LowBatteryCharging.bmp}FILE FREEFORM = 3FD97907-93F1-4349-AF3C-3B68B0A5E626 {SECTION UI = "battery_symbol_LowBattery.bmp"SECTION RAW = QcomPkg/Drivers/QcomChargerDxe/battery_symbol_LowBattery.bmp}

我们来看下入口函数:QcomChargerInitialize()

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe\QcomCharger.cEFI_STATUS QcomChargerInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){/* Required Initialization */Status = ChargerPlatform_Init();Status = gBS->InstallMultipleProtocolInterfaces( &ImageHandle,&gQcomChargerProtocolGuid, &QcomChargerProtocolImplementation, NULL );return Status;
}

可以看出,在入口函数中,只做了两件事,第一个为充电初化,第二就是将QcomChargerProtocolImplementation结构体函数指针与gQcomChargerProtocolGuid 绑定在一起,我们来看下充电初始化函数中做了啥。

  1. 获取平台类型,如: EFI_PLATFORMINFO_TYPE_MTP
  2. 加载充电配置文件 QcomChargerCfg.cfg,解析的内容保存在QCOM_CHARGER_PLATFORM_CFGDATA_TYPE gChargerPlatformCfgData 结构体中
  3. 根据gChargerPlatformCfgData 中的配置信息,初始化充电相关的lib 库
  4. 获取充电配置文件中的 BootToHLOSThresholdInMv = 3600, OsStandardBootSocThreshold = 7
  5. 使能充电硬件 JEITA
  6. 根据配置文件决定是否开启看门狗
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe\QcomChargerPlatform.c/* ChargerPlatform_Init(): This function locates and initializes ADC Protocal, Charger Protocal and other Protocols
that are needed for that specific platform. It also loads the cfg file and initialize charger and FG accordingly. */
EFI_STATUS ChargerPlatform_Init( VOID )
{// 1. 获取平台类型,如: EFI_PLATFORMINFO_TYPE_MTPStatus |= GetPlatformType(&PlatformType);// 2. 加载充电配置文件 QcomChargerCfg.cfg,解析的内容保存在gChargerPlatformCfgData 结构体中  /* Load CFG file */Status |= ChargerPlatformFile_ReadDefaultCfgData();// 3. 根据gChargerPlatformCfgData 中的配置信息,初始化充电相关的lib 库 /* Init Charger lib configs */Status |= ChargerLibCommon_InitConfiguration((chargerlib_cfgdata_type*)&gChargerPlatformCfgData);Status = ChargerPlatformFile_FileLogInit(gChargerPlatformCfgData);/* Init Charger lib */Status |= ChargerLibCommon_Init();// 4. 获取充电配置文件中的 BootToHLOSThresholdInMv = 3600, OsStandardBootSocThreshold = 7gThresholdVbatt = gChargerPlatformCfgData.BootToHLOSThresholdInMv;    gThresholdSoc   = gChargerPlatformCfgData.BootToHLOSThresholdInSOC;// 5. 使能充电硬件 JEITA /* Enable HW JEITA*/Status |= ChargerLib_EnableHWJeita(TRUE);// 6. 根据配置文件决定是否开启看门狗 /*Disable Charger wdog before applying config based wdog setting*/Status = ChargerLib_EnableWdog(FALSE);if((QCOM_CHG_WDOG_DISABLE_ON_EXIT == gChargerPlatformCfgData.EnableChargerWdog)|| (QCOM_CHG_WDOG_LEAVE_ENABLED_ON_EXIT == gChargerPlatformCfgData.EnableChargerWdog)){/* Enable Charger Watchdog*/Status = ChargerLib_EnableWdog(TRUE);}return Status;
}

充电配置文件实际路径为 QcomChargerConfig_VbattTh_Sdm660.cfg,在该文件中定义了一系列的充电参数。

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\LA\Sdm660Pkg.fdfFILE FREEFORM = 45FE4B7C-150C-45da-A021-4BEB2048EC6F {SECTION UI = "QcomChargerCfg.cfg"SECTION RAW = QcomPkg/Drivers/QcomChargerDxe/QcomChargerConfig_VbattTh_Sdm660.cfg}# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe\QcomChargerConfig_VbattTh_Sdm660.cfg# 充电电流配置为200mA      Charging termination current in milliamps
ChargingTermCurrent = 200# 电流ID电压误差容量8%    Battery ID Tolerance Percentage 8%
BatteryIdTolerance = 8# 开机电压3.6v  Boot device to HLOS in case of unsupported battery or battery emulator. In millivolt*/
BootToHLOSThresholdInMv = 3600# 低电压3.2v    Lowest Voltage at which device should shutdown gracefully value in mV
EmergencyShutdownVbatt = 3200# 温升配置
ProgramBattThermCoeffs = TRUE
BattThermC1           = A1
BattThermC2           = 50
BattThermC3           = FF# 是否加载电池配置文件     Load Fuel Gauge Battery Profile profile for SOC estimation and accuracy
LoadBatteryProfile   = TRUE# 空电池电压2.85v   Vbat Empty threshold in mv
VBtEmpty = 2850

3.2 更改充电状态 pQcomChargerProtocol->TakeAction()

在函数EFI_QcomChargerTakeAction() 中调用的是ChargerPlatform_TakeAction() 函数,我们来分析下:
电池状态的处理情况,分类如下几种:

  1. 直接关机:QCOM_CHARGER_PLATFORM_ACTION_CRITICALQCOM_CHARGER_PLATFORM_ACTION_SHUTDOWN
  2. 开始/停止 充电:QCOM_CHARGER_PLATFORM_ACTION_START_CHARGINGQCOM_CHARGER_PLATFORM_ACTION_STOP_CHARGING
  3. 继续充电: QCOM_CHARGER_PLATFORM_ACTION_CONTINUE
  4. 加载充电曲线配置文件:QCOM_CHARGER_PLATFORM_ACTION_PROFILE_LOAD
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe\QcomChargerPlatform.c
EFI_STATUS ChargerPlatform_TakeAction(QCOM_CHARGER_PLATFORM_ACTION_TYPE ChargingAction, CONST QCOM_CHARGER_PLATFORM_ACTION_INFO *pChargerActionInfo)
{switch (ChargingAction){case QCOM_CHARGER_PLATFORM_ACTION_CRITICAL:CHARGER_FILE_UART_DEBUG((EFI_D_WARN, "QcomChargerDxe::%a Critical Error occurred. Shutting down \r\n", __FUNCTION__));ChargerLib_ForceSysShutdown(CHGAPP_RESET_AFP);break;case QCOM_CHARGER_PLATFORM_ACTION_SHUTDOWN://print error message and trigger system shutdown//These errors will only be checked and handled when battery voltage is not high enough to boot and uefi charging is needed.ChargerLib_ForceSysShutdown(CHGAPP_RESET_SHUTDOWN);break;case QCOM_CHARGER_PLATFORM_ACTION_START_CHARGING:/* Assign entry voltage on warm boot and soc after profile is loaded or not loaded as in warm/cold boot */ChargerLib_InitializeCharging();break;case QCOM_CHARGER_PLATFORM_ACTION_STOP_CHARGING:if(TRUE == gChargingInitialized){ChargerLib_ChargerEnable(FALSE);gChargingInitialized = FALSE;}if(gChargerPlatformCfgData.ChargerLedConfig){/* Turn Off Charging */bToggleLed = FALSE;ChargerLib_LedOn(bToggleLed);           // 关闭LED灯}break;case QCOM_CHARGER_PLATFORM_ACTION_NO_CHARGE_WAIT:ChargerLib_HandleNoChargeAndWait();if(gChargerPlatformCfgData.EnableChargerWdog){/* Pet the watchdog if feature is enabled */ChargerLib_PetChgWdog();}break;case QCOM_CHARGER_PLATFORM_ACTION_CONTINUE:if(gChargerPlatformCfgData.EnableChargerWdog){/* Pet the watchdog if feature is enabled */ChargerLib_PetChgWdog();}if(gChargerPlatformCfgData.ChargerLedConfig){/*  DEBUG ONLY *//* CHARGER_DEBUG((EFI_D_WARN, "QcomChargerDxe::%a SWLedToggleConfig = %d \r\n", __FUNCTION__, gChargerPlatformCfgData.ChargerLedConfig)); */switch(gChargerPlatformCfgData.ChargerLedConfig){case QCOM_CHARGER_PLATFORM_CHARGING_LED_ON:bToggleLed = TRUE; /* Make sure to turn on flag as control can come back from wait state */ChargerLib_LedOn(bToggleLed);break;case QCOM_CHARGER_PLATFORM_CHARGING_LED_TOGGLE:ChargerLib_LedOn(bToggleLed);bToggleLed = (bToggleLed == TRUE)? FALSE: TRUE;break;case QCOM_CHARGER_PLATFORM_CHARGING_LED_OFF:/* will not reache here */default:break;}}/* Debug FG SRAM */ChargerLib_DumpSram(FALSE);break;case QCOM_CHARGER_PLATFORM_ACTION_GOOD_TO_BOOT:/* Assign Exit voltage and soc */gChargerSharedInfo.uefi_exit_mV    = CurrentBatteryStatus.BatteryVoltage;if (gChargerPlatformCfgData.FgCfgData.LoadBatteryProfile == TRUE){gChargerSharedInfo.uefi_exit_soc = CurrentBatteryStatus.StateOfCharge;}else{gChargerSharedInfo.uefi_exit_soc   = QCOM_CHARGER_INVALID_VALUE_MARKER;}CHARGER_DEBUG(( EFI_D_WARN, "QcomChargerDxe:: %a Saving Exit VBat = %d Soc = %d \r\n", __FUNCTION__,CurrentBatteryStatus.BatteryVoltage, CurrentBatteryStatus.StateOfCharge));/* Save Smem Info ignoring return status as XBL loader changes are not ready yet*/ChargerPlatform_SaveChargeInfoToSmem(&gChargerSharedInfo);if (gChargerPlatformCfgData.WiPowerCfgData.WipowerEnabled){if (gChargerPlatformCfgData.WiPowerCfgData.DCINDisableOnExit){//suspend DCIn when wipower flags are set.(void)ChargerLib_DcinSuspend(TRUE);}if (QCOM_CHARGER_PLATFORM_CHARGER_ATTACHED_DCIN == pChargerActionInfo->ChargerAttached){(void)ChargerLib_ToggleWipowerShutDownLatch();}}/* Turn Off Charging */bToggleLed = FALSE;ChargerLib_LedOn(bToggleLed);break;case QCOM_CHARGER_PLATFORM_ACTION_PROFILE_LOAD:/* Assign only entry voltage for smem before profile load, soc will be update after profile is loaded in next action which is start charging action*/gChargerSharedInfo.uefi_entry_mV    = CurrentBatteryStatus.BatteryVoltage;ChargerPlatform_LoadProfile(pChargerActionInfo);break;case QCOM_CHARGER_PLATFORM_ACTION_SHUTDOWN_USB_DC_PON_DISABLED://print error message and trigger system shutdown//These errors will only be checked and handled when battery voltage is not high enough to boot and uefi charging is needed.CHARGER_FILE_UART_DEBUG((EFI_D_WARN, "QcomChargerDxe::%a Waiting for %d s \r\n", __FUNCTION__, QCOM_CHARGER_IDLE_WAIT_DURATION/QCOM_CHARGER_MS_TO_S));WaitForTimeout (QCOM_CHARGER_IDLE_WAIT_DURATION, TIMEOUT_WAIT_FOR_KEY, NULL);ChargerLib_ForceSysShutdown(CHGAPP_RESET_SHUTDOWN_USB_DC_PON_DISABLED);break;break;default:CHARGER_DEBUG(( EFI_D_WARN, "QcomChargerDxe:: %a Action Passed = %d \r\n", __FUNCTION__,ChargingAction));break;}return EFI_SUCCESS;
}
3.2.1直接关机 CRITICAL、SHUTDOWN

直接关机调用的函数为:ChargerLib_ForceSysShutdown(),其主要工作流程为:

  1. 停止充电,关闭充电LED灯
  2. 如果是冷启动EfiResetCold,则清空串口缓冲区内容
  3. 初始化PMIC USB 充电驱动,配置USB为UFP模式,既从VBUS取电,且可以当作U盘提供数据
  4. 根据 ShutdownType 配置ResetType ,接着根据ResetType 调用ResetSystem() 重启系统
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Library\ChargerLib\ChargerLibCommon.c
/*This API initiates different types of reset */
EFI_STATUS ChargerLib_ForceSysShutdown(ChgAppSysShutdownType ShutdownType)
{// 1. 停止充电,关闭充电LED灯/*Disable charging*/CHARGER_DEBUG((EFI_D_WARN, "ChargerLib:: %a Disable chrging ShtDwn Type = %d \r\n", __FUNCTION__, ShutdownType));ChargerLib_ChargerEnable(FALSE);/* Turn Off Charging Led */ChargerLib_LedOn(FALSE);// 2. 如果是冷启动EfiResetCold,则清空串口缓冲区内容if (!RETAIL){/* Flush serial buffer in production mode */SerialPortFlush();CHARGER_DEBUG((EFI_D_WARN, "ChargerLib:: %a SerialPortFlush Shutdown Type = %d \r\n", __FUNCTION__, ShutdownType));SerialPortFlush();}/* Clean up */Status = ChargerLib_Exit(ChargerInfo.ChargerPmicInfo.PmicIndex);// 3. 初始化PMIC USB 充电驱动,配置USB为UFP模式,既从VBUS取电,且可以当作U盘提供数据  // Get the PMIC USB charger protocol if not already available.Status = gBS->LocateProtocol(&gQcomPmicUsbProtocolGuid, NULL, (void**)&PmicUsbProtocol);if(PmicUsbProtocol){/* PMi8998 auto powers on upon shutdown with Type-A to C cable attached */Status |= PmicUsbProtocol->UsbCSetPortRole(EFI_PM_USB_TYPEC_ROLE_UFP);/* delay before shutting down in order to ensure that VBUS discharges well below the 1V coarse detect threshold accuracy. */gBS->Stall(20000); //stall 20mS}// 4. 根据 ShutdownType 配置ResetType ,接着根据ResetType 调用ResetSystem() 重启系统switch (ShutdownType){case CHGAPP_RESET_SHUTDOWN:        ResetType = EfiResetShutdown;break;case CHGAPP_RESET_AFP:          // AFP 模式CHARGER_DEBUG((EFI_D_WARN, "ChargerLib:: %a CHGAPP_RESET_AFP.\r\n", __FUNCTION__));if (EFI_SUCCESS != ChargerLib_EnableAfpMode()){Status |= PmicPwronProtocol->EnableEdgePonTrigger(PM_DEVICE_0, EFI_PM_PON_OPTION_PON1_REDGE_PON, TRUE);}ResetType = EfiResetShutdown;break;case CHGAPP_RESET_COLD:ResetType = EfiResetCold;break;case CHGAPP_RESET_SHUTDOWN_USB_DC_PON_DISABLED:/*Disable USB as PON trigger and issue shut down *//* TBD to make sure on PON1 disabling */Status |= PmicPwronProtocol->EnableEdgePonTrigger(PM_DEVICE_0, EFI_PM_PON_OPTION_PON1_REDGE_PON, TRUE);Status |= PmicPwronProtocol->EnableEdgePonTrigger(PM_DEVICE_0, EFI_PM_PON_OPTION_DC_CHG_REDGE_PON, TRUE);Status |= PmicPwronProtocol->EnableEdgePonTrigger(PM_DEVICE_0, EFI_PM_PON_OPTION_USB_CHG_REDGE_PON, TRUE);Status |= PmicPwronProtocol->EnableEdgePonTrigger(PM_DEVICE_0, EFI_PM_PON_OPTION_CBLPWR_FEDGE_PON, TRUE);ResetType = EfiResetShutdown;break;case CHGAPP_RESET_NONE:case CHGAPP_RESET_INVALID:default:     return Status;}// 5.     /* Reset  Device*/gRT->ResetSystem(ResetType, EFI_SUCCESS, 0, NULL);return Status;
}
3.2.2 开始/停止 充电 START_CHARGING、STOP_CHARGING

先来看下 START_CHARGING 的流程:

  1. 使能 PMIC 寄存器充电位
  2. 获取当前USB类型,如 TYPEC、 MICRO_USB
  3. 获取充电类型,如 SDP,OCP,CDP,DCP 等
  4. 如果是SDP,则获取最大电流,如果是900mA时,说明是SDP_3_0模式,否则是 SDP_2_0模式
  5. 配置USB 模式
  6. 配置最大充电电流
  7. 配置PMIC 寄存器来使能充电
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Library\ChargerLib\ChargerLibCommon.c
EFI_STATUS ChargerLib_InitializeCharging( VOID )
{// 1. 使能 PMIC 寄存器充电位Status |= gBS->LocateProtocol( &gQcomPmicSchgProtocolGuid, NULL,(VOID **)&PmicSchgProtocol );Status = PmicSchgProtocol->SetChargeCmdBit(ChargerInfo.ChargerPmicInfo.PmicIndex, FALSE); ===========>pm_schg_chgr_set_chgr_cmd_type(PmicDeviceIndex, PM_SCHG_CHGR_CMD_CHARGING_ENABLE, Enable);<===========  // 2. 获取当前USB类型,如 TYPEC、 MICRO_USBStatus = gBS->LocateProtocol(&gQcomPmicUsbProtocolGuid,NULL,(void**)&PmicUsbProtocol);Status = PmicUsbProtocol->GetUSBConnectorType(&pUSBConnectorType); Status = PmicUsbProtocol->SetUSBConnectorType(pUSBConnectorType);  Status |= ChargerLib_ConfigureUsbCurrentSetting();==========>// 3. 获取充电类型,如 SDP,OCP,CDP,DCP 等Status |= ChargerLib_GetPortType(&PortType);if(CHARGERLIB_CHG_PORT_SDP_CHARGER == PortType){// 4. 如果是SDP,则获取最大电流,如果是900mA时,说明是SDP_3_0模式,否则是 SDP_2_0模式 Status |= GetSdpMaxCurrent(&MaxCurrent);if(USB_3_IUSB_MAX == MaxCurrent){IclModeType = CHARGERLIB_USB_ICL_MODE_SDP_3_0;UsbInputCurrentLimitInMa = USB_3_IUSB_MAX;}else{IclModeType = CHARGERLIB_USB_ICL_MODE_SDP_2_0;UsbInputCurrentLimitInMa = USB_2_IUSB_MAX;}}// 5. 配置USB 模式Status |= ChargerLib_SetUSbICLMode(IclModeType);// 6. 配置最大充电电流Status |= ChargerLib_SetMaxUsbCurrent(UsbInputCurrentLimitInMa); <==========// 7. 配置PMIC 寄存器来使能充电CHARGER_DEBUG((EFI_D_WARN, "ChargerLib:: %a Enabling charging \r\n", __FUNCTION__));Status |= ChargerLib_ChargerEnable(TRUE);gChargingEnabled = TRUE;
}

接下来看下停止充电 STOP_CHARGING,它的流程相对比较简单,
主要就是调用ChargerLib_ChargerEnable 配置PMIC寄存器,且停止LED灯。

ChargerLib_ChargerEnable(FALSE);     // 停止充电
ChargerLib_LedOn(bToggleLed);           // 关闭LED灯
3.2.3 继续充电 CONTINUE

Continue 中主要是做了两件事,给看门狗喂狗,更新LED的状态(包括LED开启、闪烁这两种)

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Library\ChargerLib\ChargerLibCommon.c
/* Pet the watchdog if feature is enabled */
ChargerLib_PetChgWdog();
=====> Status = PmicSchgProtocol->PetChgWdog(ChargerInfo.ChargerPmicInfo.PmicIndex);switch(gChargerPlatformCfgData.ChargerLedConfig)
{case QCOM_CHARGER_PLATFORM_CHARGING_LED_ON:            // 点亮LED灯bToggleLed = TRUE; /* Make sure to turn on flag as control can come back from wait state */ChargerLib_LedOn(bToggleLed);break;case QCOM_CHARGER_PLATFORM_CHARGING_LED_TOGGLE:     // LED灯闪烁ChargerLib_LedOn(bToggleLed);bToggleLed = (bToggleLed == TRUE)? FALSE: TRUE;break;
}

3.3 获取当前充电状态 pQcomChargerProtocol->GetChargingAction()

  1. 检测充电相关错误,如函数运行失败直接通关机。
  2. 根据前面的错误进行不同的处理:电池不存在,调试主板,温度异常,电池电压异常,充电电源异常
  3. 默认LED 灯为闪烁模式。
  4. 加载电池曲线参数
  5. 检测充电过程的所有错误,并进行相应的处理
  6. 获取电池状态
  7. 获取当前充电的输入方式,如 USB、DCIN
  8. 当一切准备就绪时,配置 pActionType=QCOM_CHARGER_PLATFORM_ACTION_START_CHARGING
  9. 如果状态不符,则获取当前的充电状态
  10. 如果是不在充电,则dump打印寄存器信息,如果是无线充电,配置为NO_CHARGE_WAIT,否则配置为START_CHARGING开始充电,如果是正在充电,则配置为 CONTINUE
  11. 如果充电线已经连接,则触发重新插入动作
  12. 检查当前是否满足正常开机条件,如果满足的话,更新 pActionTypeQCOM_CHARGER_PLATFORM_ACTION_GOOD_TO_BOOT
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe\QcomChargerPlatform.cEFI_STATUS ChargerPlatform_GetChargingAction(QCOM_CHARGER_PLATFORM_ACTION_TYPE *pActionType, QCOM_CHARGER_PLATFORM_ACTION_INFO *pChargerActionInfo)
{// 1. 检测充电相关错误,如函数运行失败直接通关机。/* Get Error like debug board or battery not detected first */Status |= ChargerLib_GetErrors(&ErrorType);// 2. 根据前面的错误进行不同的处理:电池不存在,调试主板,温度异常,电池电压异常,充电电源异常if((CHARGERLIB_CHARGING_ERROR_BATTERY_NOT_DETECTED == ErrorType ) || (CHARGERLIB_CHARGING_ERROR_DEBUG_BOARD == ErrorType ) ||(CHARGERLIB_DEVICE_ERROR == ErrorType ) || (CHARGERLIB_CHARGING_ERROR_UNKNOWN_BATTERY == ErrorType )){Status = ChargerLib_GetErrorAction(ErrorType, (((CHARGERLIB_ERROR_ACTION_TYPE*)pActionType)));PrevChargerAction = *pActionType;/*If there is a battery error, return */return Status;}// 3. 默认LED 灯为闪烁模式。/* Assign Led config to toggle led */pChargerActionInfo->LedConfigType = (QCOM_CHARGER_PLATFORM_CHARGING_LED_CONFIG_TYPE)gChargerPlatformCfgData.ChargerLedConfig;// 4. 加载电池曲线参数if(PrevChargerAction == QCOM_CHARGER_PLATFORM_ACTION_INVALID ){/* Load profile if SocBasedboot is true or profile load is enabled */if((gChargerPlatformCfgData.SocBasedBoot == TRUE ) || (gChargerPlatformCfgData.FgCfgData.LoadBatteryProfile == TRUE )){Status = ChargerPlatform_ProfileLoadingInit(pActionType, pChargerActionInfo);PrevChargerAction = *pActionType;/* Return since action is decided for this Invalid charging action case */return Status;}}// 5. 检测充电过程的所有错误,并进行相应的处理Status |= ChargerLib_GetErrors(&ErrorType);// 6. 获取电池状态Status |= ChargerLib_GetBatteryStatus((chargerlib_batt_status_info*)&CurrentBatteryStatus);// 7. 获取当前充电的输入方式,如 USB、DCINStatus = ChargerLib_GetChargingPath(&pChargerActionInfo->ChargerAttached);/* Assign status info for caller */pChargerActionInfo->BattStsInfo = CurrentBatteryStatus;// 8. 当一切准备就绪时,配置 pActionType=QCOM_CHARGER_PLATFORM_ACTION_START_CHARGINGif ((QCOM_CHARGER_PLATFORM_CHARGER_ATTACHED_USB  == pChargerActionInfo->ChargerAttached) &&((QCOM_CHARGER_PLATFORM_ACTION_INVALID        == PrevChargerAction ) || (QCOM_CHARGER_PLATFORM_ACTION_PROFILE_LOAD   == PrevChargerAction ) || (QCOM_CHARGER_PLATFORM_ACTION_NO_CHARGE_WAIT == PrevChargerAction ))){*pActionType = QCOM_CHARGER_PLATFORM_ACTION_START_CHARGING; }else{// 9. 如果状态不符,则获取当前的充电状态Status = ChargerLib_GetChargingStatus(&ChargingEnabled);// 10. 如果是不在充电,则dump打印寄存器信息,如果是无线充电,配置为NO_CHARGE_WAIT,否则配置为START_CHARGING开始充电,如果是正在充电,则配置为 CONTINUEif(FALSE == ChargingEnabled){/*Charger register dump in case need to determine why charging is disabled*/Status |= ChargerLib_DumpChargerPeripheral();if (gChargerPlatformCfgData.WiPowerCfgData.WipowerEnabled && (QCOM_CHARGER_PLATFORM_CHARGER_ATTACHED_DCIN == pChargerActionInfo->ChargerAttached)){*pActionType = QCOM_CHARGER_PLATFORM_ACTION_NO_CHARGE_WAIT;}else{*pActionType = QCOM_CHARGER_PLATFORM_ACTION_START_CHARGING;}}else{/* Charging already started, go to continue. */*pActionType = QCOM_CHARGER_PLATFORM_ACTION_CONTINUE;/* Assign Led config to toggle led *//* pChargerActionInfo->LedConfigType = (QCOM_CHARGER_PLATFORM_CHARGING_LED_CONFIG_TYPE)gChargerPlatformCfgData.ChargerLedConfig;*/}// 11. 如果充电线已经连接,则触发重新插入动作,if (QCOM_CHARGER_PLATFORM_CHARGER_ATTACHED_USB == pChargerActionInfo->ChargerAttached){/* Check if charger was swapped/re-inserted */Status = ChargerLib_WasChargerReinserted(&bChargerReinserted);}}// 12. 检查当前是否满足正常开机条件,如果满足的话,更新 pActionType 为 QCOM_CHARGER_PLATFORM_ACTION_GOOD_TO_BOOTStatus |= ChargerPlatform_CheckIfOkToBoot(pActionType, *pChargerActionInfo, CurrentBatteryStatus);CHARGER_DEBUG(( EFI_D_WARN, "QcomChargerDxe:: %a Action Returned = %d \r\n", __FUNCTION__,*pActionType));PrevChargerAction = *pActionType;return Status;
}

3.4 显示充电图标 pQcomChargerProtocol->DisplayImage()

  1. 通过ChargerLibEvent_AsciiStrNDup() 显示当前的充电状态图标
  2. 通过DrawBmpFile() 绘制 BMP图片
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe\QcomCharger.c
EFI_STATUS EFIAPI EFI_QcomChargerDisplayImage(IN EFI_QCOM_CHARGER_DISP_IMAGE_TYPE ImageType,IN BOOLEAN ClearScreen)
{EFI_STATUS Status = EFI_SUCCESS;Status = ChargerLibEvent_DispBattSymbol((CHARGERLIB_EVENT_DISP_IMAGE_TYPE)ImageType, ClearScreen); return Status;
}EFI_STATUS ChargerLibEvent_DispBattSymbol(CHARGERLIB_EVENT_DISP_IMAGE_TYPE DispImage, BOOLEAN ClearScreen)
{EFI_STATUS Status = EFI_SUCCESS;char   *str = NULL;/// 1. 通过ChargerLibEvent_AsciiStrNDup 获取图片的路径switch (DispImage){case CHARGERLIB_EVENT_DISP_IMAGE_ABOVE_THRESHOLD:str = ChargerLibEvent_AsciiStrNDup(CHARGER_BATTERY_SYMBOL_ABOVE_THRESHOLD, AsciiStrLen(CHARGER_BATTERY_SYMBOL_ABOVE_THRESHOLD));break;case CHARGERLIB_EVENT_DISP_IMAGE_NOBATTERY:str = ChargerLibEvent_AsciiStrNDup(CHARGER_BATTERY_SYMBOL_NOBATTERY, AsciiStrLen(CHARGER_BATTERY_SYMBOL_NOBATTERY));break;case CHARGERLIB_EVENT_DISP_IMAGE_NOCHARGER:str = ChargerLibEvent_AsciiStrNDup(CHARGER_BATTERY_SYMBOL_NOCHARGER, AsciiStrLen(CHARGER_BATTERY_SYMBOL_NOCHARGER));break;case CHARGERLIB_EVENT_DISP_IMAGE_LOWBATTERYCHARGING:str = ChargerLibEvent_AsciiStrNDup(CHARGER_BATTERY_SYMBOL_LOWBATTERYCHARGING, AsciiStrLen(CHARGER_BATTERY_SYMBOL_LOWBATTERYCHARGING));break;case CHARGERLIB_EVENT_DISP_IMAGE_LOWBATTERY:str = ChargerLibEvent_AsciiStrNDup(CHARGER_BATTERY_SYMBOL_LOWBATTERY, AsciiStrLen(CHARGER_BATTERY_SYMBOL_LOWBATTERY));break;case CHARGERLIB_EVENT_DISP_IMAGE_TSENS_THERMAL_SYMBOL:str = ChargerLibEvent_AsciiStrNDup(CHARGER_TSENS_THERMAL_SYMBOL, AsciiStrLen(CHARGER_TSENS_THERMAL_SYMBOL));break;default:break;}// 2. 通过DrawBmpFile() 绘制 BMP图片/* Draw BMP image with default options, the screen will be cleared and the imagewill be drawn at the center of the screen*/Status = DrawBmpFile(str, NULL, 0);// 3. 清空内存if(NULL != str ) {FreePool(str);str = NULL;}return Status;
}
3.4.1 获取图片路径 ChargerLibEvent_AsciiStrNDup()

在源码中,充电图相关的图片位于:amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomChargerDxe
编译下载到主板后,相关图片位置位于 ChargerLibDisplay.c 文件中。

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Library\ChargerLib\ChargerLibDisplay.c
#define CHARGER_BATTERY_SYMBOL_NOBATTERY            "fv1:\\battery_symbol_NoBattery.bmp"
#define CHARGER_BATTERY_SYMBOL_NOCHARGER            "fv1:\\battery_symbol_Nocharger.bmp"
#define CHARGER_BATTERY_SYMBOL_ABOVE_THRESHOLD      "fv1:\\battery_symbol_Soc10.bmp"
#define CHARGER_BATTERY_SYMBOL_LOWBATTERYCHARGING   "fv1:\\battery_symbol_LowBatteryCharging.bmp"
#define CHARGER_BATTERY_SYMBOL_LOWBATTERY           "fv1:\\battery_symbol_LowBattery.bmp"
#define CHARGER_TSENS_THERMAL_SYMBOL                "fv1:\\tsens_thermal_symbol.bmp"

我们以如下代码为例:
ChargerLibEvent_AsciiStrNDup(CHARGER_BATTERY_SYMBOL_ABOVE_THRESHOLD, AsciiStrLen(CHARGER_BATTERY_SYMBOL_ABOVE_THRESHOLD));
传入的参数为 CHARGER_BATTERY_SYMBOL_ABOVE_THRESHOLD,实际对应着图片 battery_symbol_Soc10.bmp


可以看出,在 ChargerLibEvent_AsciiStrNDup() 中主要就是在HOB中申请与字符串对应的内存并返回内存地址保存在Destk中 ,接着拷贝图片路径到其中。

static CHAR8 * ChargerLibEvent_AsciiStrNDup(CONST CHAR8 *Source, UINTN Length){CHAR8 *Dest = NULL;Dest = AllocatePool (Length + 1);==========> // Create a memory allocation HOB.BuildMemoryAllocationHob ( Hob.HandoffInformationTable->EfiFreeMemoryTop,Pages * EFI_PAGE_SIZE,EfiBootServicesData);AsciiStrnCpy(Dest, Source, Length + 1);return Dest;
}
3.4.2 绘制图片DrawBmpFile()
  1. 安装图片输出驱动
  2. 根据传入的路径,从fv固件中加载对应的图片
  3. 解析BMP图片,最终图片内容保存在 Blt地址指向的内存中(RGB格式),图片bmp头信息中的宽高保存在Height、Width中。
  4. 显示图片
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Library\BMPLib\BMPLib.c
EFI_STATUS EFIAPI DrawBmpFile (IN CHAR8 *FileName, IN BMPLIB_OPTION  Opts[] OPTIONAL,IN UINTN NumOpts)
{// 1. 安装图片输出驱动/* Ensure Graphics Output protocol is up */Status = CheckGOPAvailable();===============>Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid,NULL,(VOID**)&mGOP);<===============// 2. 根据传入的路径,从fv固件中加载对应的图片/* Load file into buffer */Status = LoadFromFV(FileName, &FileBuffer, &FileSize);// 3. 解析BMP图片,最终图片内容保存在 Blt地址指向的内存中(RGB格式),图片bmp头信息中的宽高保存在Height、Width中。/* Convert bitmap to GOP blit safe format */Status = ConvertBmpToGopBlt ( FileBuffer, FileSize,(VOID **) &Blt, &BltSize, &Height, &Width);// 4. 显示图片Status = DrawGopBltBuffer( Blt, BltSize, Height, Width, Opts, NumOpts);return Status;
}

从上可看出,显示图片的重头戏在于 DrawGopBltBuffer() 中,我们来分析下:

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Library\BMPLib\BMPLib.c
EFI_STATUS EFIAPI DrawGopBltBuffer(IN VOID *GopBlt, IN UINTN  BltSize, IN UINTN Height, IN UINTN Width, IN BMPLIB_OPTION Opts[] OPTIONAL, IN UINTN NumOpts)
{// 1. 安装图片输出驱动 /* Ensure Graphics Output protocol is up */Status = CheckGOPAvailable();// 2. 解析 opts,并清空屏幕/* Validate and Get Option: ClearScreen */Status = BmpLibGetOption(Opts, NumOpts, BmplibOptionTypeClearScreen, (VOID**)&OptClsParams);/* Validate and Get Option: Location */Status = BmpLibGetOption(Opts, NumOpts, BmplibOptionTypeLocation, (VOID**)&OptLocParams);/* Clear screen unless overriden */Status = BmpLibClearScreen(OptClsParams);/* Blit GOP buffer as specified */Status = BmpLibBltGopBuffer( GopBlt,BltSize, Height, Width, OptLocParams );return Status;
}

来分析下 BmpLibBltGopBuffer() 函数

  1. 解析图片开始绘制的坐标
static EFI_STATUS
BmpLibBltGopBuffer(IN VOID* Blt, IN UINTN BltSize, IN UINT32 Height, IN UINT32 Width, IN BMPLIB_OPTION_LOCATION_PARAMS    *OptLocParams)
{UINT32      SizeOfX = mGOP->Mode->Info->HorizontalResolution;UINT32      SizeOfY = mGOP->Mode->Info->VerticalResolution;// 1. 解析图片开始绘制的坐标/* Null location option defaults to screen center */BMPLIB_OPTION_LOCATION_COORD Location =(OptLocParams) ? OptLocParams->Location : BmplibOptionLocationCenter;/* Determine location */switch (Location) {case BmplibOptionLocationTopLeft:DestX = 0;DestY = 0;break;case BmplibOptionLocationTopCenter:DestX = (SizeOfX - Width) / 2;DestY = 0;break;case BmplibOptionLocationTopRight:DestX = (SizeOfX - Width);DestY = 0;break;case BmplibOptionLocationCenterLeft:DestX = 0;DestY = (SizeOfY - Height) / 2;break;case BmplibOptionLocationCenter:DestX = (SizeOfX - Width) / 2;DestY = (SizeOfY - Height) / 2;break;case BmplibOptionLocationCenterRight:DestX = (SizeOfX - Width);DestY = (SizeOfY - Height) / 2;break;case BmplibOptionLocationBottomLeft:DestX = 0;DestY = (SizeOfY - Height);break;case BmplibOptionLocationBottomCenter:DestX = (SizeOfX - Width) / 2;DestY = (SizeOfY - Height);break;case BmplibOptionLocationBottomRight:DestX = (SizeOfX - Width);DestY = (SizeOfY - Height);break;case BmplibOptionLocationCustom:DestX = OptLocParams->DestX;DestY = OptLocParams->DestY;break;default:return EFI_INVALID_PARAMETER;}// 2. 开始绘制图片/* Blit to screen */Status = mGOP->Blt ( mGOP,Blt,EfiBltBufferToVideo,0,0,(UINTN) DestX,(UINTN) DestY,Width,Height,Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));return Status;
}

截止目前,充电一些重要的函数就分析完了,有了这些的了解,清楚充电代码跑的流程,后面在项目时,如果遇到充电bug或要做相关的需求也就会轻松长很多。

好,充电驱动各函数分析就到此为止吧。

有关BMP图片的显示过程(gEfiGraphicsOutputProtocolGuid 和 mGOP->Blt()),
我们在后面单独拉一章一分析下。

【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析相关推荐

  1. 【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程

    [Android SDM660源码分析]- 03 - UEFI XBL GraphicsOutput BMP图片显示流程 1. GraphicsOutput.h 2. 显示驱动初化 DisplayDx ...

  2. 【Android SDM660源码分析】- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序

    [Android SDM660源码分析]- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序 一.创建DXE_DRIVER ...

  3. 【Android SDM660源码分析】- 04 - UEFI ABL LinuxLoader 代码分析

    [Android SDM660源码分析]- 04 - UEFI ABL LinuxLoader 代码分析 1. LinuxLoader.c 系列文章: <[Android SDM660开机流程] ...

  4. SpringCloud组件 源码剖析:Eureka服务注册方式流程全面分析

    在SpringCloud组件:Eureka服务注册是采用主机名还是IP地址?文章中我们讲到了服务注册的几种注册方式,那么这几种注册方式的源码是怎么实现的呢?我们带着这一个疑问来阅读本章内容能够让你更深 ...

  5. android view 源码分析,Android ViewPager源码详细分析

    1.问题 由于Android Framework源码很庞大,所以读源码必须带着问题来读!没有问题,创造问题再来读!否则很容易迷失在无数的方法与属性之中,最后无功而返. 那么,关于ViewPager有什 ...

  6. 【Android 性能优化】应用启动优化 ( 安卓应用启动分析 | Launcher 应用简介 | Launcher 应用源码简介 | Launcher 应用快捷方式图标点击方法分析 )

    文章目录 一. Launcher 应用简介 二. Launcher 应用源码简介 三. Launcher 图标点击方法分析 一. Launcher 应用简介 Launcher 应用 : Android ...

  7. Android HandlerThread 源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我们又有一 个耗时任务需要执行,我们不得不重新创建 ...

  8. Android ADB 源码分析(三)

    前言 之前分析的两篇文章 Android Adb 源码分析(一) 嵌入式Linux:Android root破解原理(二) 写完之后,都没有写到相关的实现代码,这篇文章写下ADB的通信流程的一些细节 ...

  9. android 系统源码分析

    获得Android源码后,我们来分析源码结构.源码的全部工程分为如下三个部分. ①Core Project:核心工程部分,这是建立Android系统的基础,保存在根目录的各个文件夹中. ②Extern ...

最新文章

  1. Android 判断字符串是否为空
  2. netty之ObjectSizeEstimator
  3. 经典的《JavaScript 权威指南》中的“对象”不经典
  4. RequestDispatcher
  5. Java 洛谷 P2089 烤鸡
  6. ubuntu 16.04 挂载新硬盘
  7. Golang笔记——方法
  8. python路线选择试题_python例题练习
  9. 启动子级时出错_WHO I级脑膜瘤手术或放射外科治疗后的恶性转变
  10. gis 空间分析 鸟类栖息地选取_GIS空间分析专题一:鸟儿栖息地查找
  11. 入侵他人电脑四个步骤_增长的七个步骤利用数据入侵您的业务
  12. 免费下载3小时学会Excel数据处理视频教程
  13. java 找出重复的数字
  14. linux 下查看硬件信息(mac,IP地址,硬盘型号,序列号等)
  15. 5.24 通过高级筛选功能将筛选结果放置在其它位置 [原创Excel教程]
  16. 【管理学】行业KOL——关键意见领袖(达人效应)
  17. 定制合成:热激发延迟荧光材料PPZ-3TPT、PPZ-4TPT、PPZ-DPS或PXZ-DPS、DMAC-DPS
  18. python算法详解 张玲玲_Python算法详解
  19. spring boot整合JDBC
  20. 微信开发平台第三方平台86004无效微信号

热门文章

  1. 分治法解决最近点对问题
  2. 域名代购还需要关注到全方位宣传
  3. 磊科nw362 linux驱动下载,磊科nw362无线网卡驱动
  4. 【PHP基础知识】——常用字符串处理函数总结
  5. Matplotlib学习之subplots函数
  6. 7寸显示器 树莓派4b,7寸屏幕
  7. 【软件测试】8年资深测试解析,软件测试行业情报,风风雨雨......
  8. AirSim在Windows下环境搭建
  9. 少儿编程scratch -- 提高篇
  10. 卸载win10自带应用