UEFI开发学习20 – Setup 互动控制

概述

软件开发是有层次之分的,如网站程序中的前端和后端,前者注重于UI设计,后者则注重功能和逻辑的实现。网站前端语言有HTML、CSS、JS等,后端语言如PHP,Java等。此外,除了前后端,还有底层与上层之分。底层多为跟硬件打交道的固件开发,上层则是运行于OS中的APP开发,UEFI属于底层,底层中也分前后端,如UEFI中的Setup界面便是前端,读写寄存器,枚举设备等由C语言实现的则是后端。

setup 简介

Setup是UEFI为了方便对一些功能进行配置而设计的一个界面。同大多数的软件设计架构一样,UEFI也采用了前后端分离的架构,界面布局只需通过编辑VFR文件即可。

在UEFI的开发过程中,可能会需要在Setup界面做一些动态控制,如弹框,开关联动等,而VFR只是一种界面描述语言,无法直接动态控制,需要借助C语言来实现,那C语言如何得到VFR文件的相关信息呢?这一篇就来探究探究前端与后端的互动是如何实现的。

INTERACTIVE & questionid / key

INTERACTIVE 是Setup Item中的一个标志位,用于表示某Item支持互动功能;questionid / key 则是一个全局的Item id,setup中有很多的item,假如有多个item做互动功能,那就需要一个id来标识,每个id表示一个item。questionid和key的用途是一样的,区别是一个用于oneof的item,一个是用于text。

实例分析

SETUP首页有个Continue的Item,按下该Item将执行可引导程序,如下:

如何实现的呢?

在IntelFrameworkModulePkg\Universal\BdsDxe\FrontPageVfr.Vfr文件中,可找到相应的代码,如下:

text
  help    = STRING_TOKEN(STR_CONTINUE_HELP),
  text    = STRING_TOKEN(STR_CONTINUE_PROMPT),
  flags   = INTERACTIVE,
  key     = FRONT_PAGE_KEY_CONTINUE;

可看到,在flags处使用了INTERACTIVE来表示这是一个互动Item,之下还有一个key,它是一个全局id。通过搜索这个FRONT_PAGE_KEY_CONTINUE可定位到处理该Item的C文件IntelFrameworkModulePkg\Universal\BdsDxe\FrontPage.c:

EFI_STATUS
EFIAPI
FrontPageCallback (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  EFI_BROWSER_ACTION                     Action,
  IN  EFI_QUESTION_ID                        QuestionId,
  IN  UINT8                                  Type,
  IN  EFI_IFR_TYPE_VALUE                     *Value,
  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
  )
{
  CHAR8                         *LangCode;
  CHAR8                         *Lang;
  UINTN                         Index;

  if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) {
    //
    // All other action return unsupported.
    //
    return EFI_UNSUPPORTED;
  }
  
  gCallbackKey = QuestionId;

  if (Action == EFI_BROWSER_ACTION_CHANGED) {
    if ((Value == NULL) || (ActionRequest == NULL)) {
      return EFI_INVALID_PARAMETER;
    }

    switch (QuestionId) {
    case FRONT_PAGE_KEY_CONTINUE:
      //
      // This is the continue - clear the screen and return an error to get out of FrontPage loop
      //
      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
      break;
    ......
  }
}

这个函数是如何跟continue这个item联系起来的呢?追踪这个函数FrontPageCallback ,最后可看到:

//
// Install Device Path Protocol and Config Access protocol to driver handle
//
Status = gBS->InstallMultipleProtocolInterfaces (
                &gFrontPagePrivate.DriverHandle,
                &gEfiDevicePathProtocolGuid,
                &mFrontPageHiiVendorDevicePath,
                &gEfiHiiConfigAccessProtocolGuid,
                &gFrontPagePrivate.ConfigAccess,
                NULL
                );
ASSERT_EFI_ERROR (Status);

//
// Publish our HII data
//
gFrontPagePrivate.HiiHandle = HiiAddPackages (
                                &gFrontPageFormSetGuid,
                                gFrontPagePrivate.DriverHandle,
                                FrontPageVfrBin,
                                BdsDxeStrings,
                                NULL
                                );
if (gFrontPagePrivate.HiiHandle == NULL) {
  return EFI_OUT_OF_RESOURCES;
}

FrontPageCallback 被放入 gFrontPagePrivate.ConfigAccess 中,然后安装到 gFrontPagePrivate.DriverHandle,接着通过 HiiAddPackages 把continue所在form与 DriverHandle 绑定起来,这便是VFR与C交互的整个实现过程了。

实例

实现一个联动控制Item,即当Item1的值变化时,Item2也跟着变化。

MdeModulePkg\Universal\DriverSampleDxe\Vfr.vfr文件添加:

oneof varid   = MyIfrNVData.Ay123_T1,    
  questionid = 0x9876,
  prompt  = STRING_TOKEN(STR_AY123_TEST1),
  help    = STRING_TOKEN(STR_AY123_HELP),
  flags   = INTERACTIVE,   
  option text = STRING_TOKEN(STR_ENABLE), value = 0x0, flags = DEFAULT;
  option text = STRING_TOKEN(STR_DISABLE), value = 0x1, flags = 0;
endoneof;

oneof varid   = MyIfrNVData.Ay123_T2,    
  questionid = 0x9875,
  prompt  = STRING_TOKEN(STR_AY123_TEST2),
  help    = STRING_TOKEN(STR_AY123_HELP),
  flags   = INTERACTIVE,   
  option text = STRING_TOKEN(STR_ENABLE), value = 0x0, flags = DEFAULT;
  option text = STRING_TOKEN(STR_DISABLE), value = 0x1, flags = 0;
endoneof;

subtitle text = STRING_TOKEN(STR_SUBTITLE_TEXT2);

MdeModulePkg\Universal\DriverSampleDxe\NVDataStruc.h 中的 DRIVER_SAMPLE_CONFIGURATION 添加:

UINT8   Ay123_T1;
UINT8   Ay123_T2;

MdeModulePkg\Universal\DriverSampleDxe\VfrStrings.uni 添加:

#string STR_AY123_TEST1                #language en-US "Ay123 Test 1"
#string STR_AY123_TEST2                #language en-US "Ay123 Test 2"
#string STR_AY123_HELP                 #language en-US "Ay123 Test help"
#string STR_ENABLE                     #language en-US "Enable"
#string STR_DISABLE                    #language en-US "Disable"

MdeModulePkg\Universal\DriverSampleDxe\DriverSample.c 中case 0x1330下添加

case 0x9876:
  Configuration = AllocateZeroPool (sizeof (DRIVER_SAMPLE_CONFIGURATION));
  ASSERT (Configuration != NULL);
  HiiGetBrowserData (&gDriverSampleFormSetGuid, VariableName, sizeof (DRIVER_SAMPLE_CONFIGURATION), (UINT8 *) Configuration);
  Configuration->Ay123_T2 = Value->u8;

  //
  // Update uncommitted data of Browser
  //
  HiiSetBrowserData (
    &gDriverSampleFormSetGuid,
    VariableName,
    sizeof (DRIVER_SAMPLE_CONFIGURATION),
    (UINT8 *) Configuration,
    NULL
    );

  break;

最终效果如下:

版权声明:
作者:bin
链接:https://ay123.net/mystudy/1293/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录