我们自定义的模块,有时候需要有一些用户可以调节的配置,这些配置可以放到Setup界面中,修改之后重启有效。
本文要介绍的就是如何在Setup界面中嵌入一个自定义的配置界面。
本文以MdeModulePkg\Universal\DriverSampleDxe\DriverSampleDxe.inf为例进行说明。
代码可以在https://gitee.com/jiangwei0512/vUDK2017下载到。
实际上该模块已经提供了比较完全的Setup示例。
DriverSampleDxe.inf提供了两个配置界面(Vfr.vfr):
以及(Inventory.vfr):
前者对应的VFR代码是Vfr.vfr,后者对应的VFR代码是Inventory.vfr,两者还分别有一个UNI文件与它对应。
关于VFR和UNI的说明可以参考BIOS/UEFI基础——UEFI用户交互界面使用说明之VFR文件和BIOS/UEFI基础——UEFI用户交互界面使用说明之UNI文件。
这里不再对显示与相应的VFR、UNI代码做说明。
DriverSampleDxe.inf的初始化位于MdeModulePkg\Universal\DriverSampleDxe\DriverSample.c文件中的DriverSampleInit()函数,该函数主要进行了如下的操作:
1. 初始化结构体DRIVER_SAMPLE_PRIVATE_DATA变量mPrivateData,其结构如下所示:
typedef struct {
UINTN Signature;
EFI_HANDLE DriverHandle[2];
EFI_HII_HANDLE HiiHandle[2];
DRIVER_SAMPLE_CONFIGURATION Configuration;
MY_EFI_VARSTORE_DATA VarStoreConfig;
//
// Name/Value storage Name list
//
EFI_STRING_ID NameStringId[NAME_VALUE_NAME_NUMBER];
EFI_STRING NameValueName[NAME_VALUE_NAME_NUMBER];
//
// Consumed protocol
//
EFI_HII_DATABASE_PROTOCOL *HiiDatabase;
EFI_HII_STRING_PROTOCOL *HiiString;
EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting;
EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *HiiKeywordHandler;
EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2;
//
// Produced protocol
//
EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
} DRIVER_SAMPLE_PRIVATE_DATA;
该结构体最重要的是那些Protocol,其中前面5个(Consumed protocol)是UEFI中的UI框架所提供的,本模块依赖于这些Protocol。最后1个(Produced protocol)是本模块提供的,用来规定了本模块提供的Setup界面的基本操作形式。
另外,VarStoreConfig和Configuration用来存放与Setup界面有关的变量,因此本模块还需要依赖于变量相关的Protocol(这里使用的是gEfiVariableArchProtocolGuid和gEfiVariableWriteArchProtocolGuid)。
NameStringId和NameValueName目前还不知道是做什么用的。
2. 安装两个界面的DevicePath和EFI_HII_CONFIG_ACCESS_PROTOCOL。
3. 设置界面上的某些需要动态更新的值:
//
// Update the device path string.
//
NewString = ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*)&mHiiVendorDevicePath0, FALSE, FALSE);
if (HiiSetString (HiiHandle[0], STRING_TOKEN (STR_DEVICE_PATH), NewString, NULL) == 0) {
DriverSampleUnload (ImageHandle);
return EFI_OUT_OF_RESOURCES;
}
if (NewString != NULL) {
FreePool (NewString);
}
//
// Very simple example of how one would update a string that is already
// in the HII database
//
NewString = L"700 Mhz";
if (HiiSetString (HiiHandle[0], STRING_TOKEN (STR_CPU_STRING2), NewString, NULL) == 0) {
DriverSampleUnload (ImageHandle);
return EFI_OUT_OF_RESOURCES;
}
HiiSetString (HiiHandle[0], 0, NewString, NULL);
4. 处理变量,这里是mPrivateData->Configuration和mPrivateData->VarStoreConfig这两个变量以及NameStringId和NameValueName,对应到Vfr.vfr中的变量:
//
// Define a Buffer Storage (EFI_IFR_VARSTORE)
//
varstore DRIVER_SAMPLE_CONFIGURATION, // This is the data structure type
varid = CONFIGURATION_VARSTORE_ID, // Optional VarStore ID
name = MyIfrNVData, // Define referenced name in vfr
guid = DRIVER_SAMPLE_FORMSET_GUID; // GUID of this buffer storage
//
// Define a EFI variable Storage (EFI_IFR_VARSTORE_EFI)
//
efivarstore MY_EFI_VARSTORE_DATA,
attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, // EFI variable attribures
name = MyEfiVar,
guid = DRIVER_SAMPLE_FORMSET_GUID;
//
// Define a Name/Value Storage (EFI_IFR_VARSTORE_NAME_VALUE)
//
namevaluevarstore MyNameValueVar, // Define storage reference name in vfr
name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME0), // Define Name list of this storage, refer it by MyNameValueVar[0]
name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME1), // Define Name list of this storage, refer it by MyNameValueVar[1]
name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME2), // Define Name list of this storage, refer it by MyNameValueVar[2]
guid = DRIVER_SAMPLE_FORMSET_GUID; // GUID of this Name/Value storage
5. 之后创建了一个事件:
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
EfiEventEmptyFunction,
NULL,
&gEfiIfrRefreshIdOpGuid,
&mEvent
);
ASSERT_EFI_ERROR (Status);
原因不明。
6. 之后就是注册按键:
//
// Example of how to use BrowserEx protocol to register HotKey.
//
Status = gBS->LocateProtocol (&gEdkiiFormBrowserExProtocolGuid, NULL, (VOID **) &FormBrowserEx);
if (!EFI_ERROR (Status)) {
//
// First unregister the default hot key F9 and F10.
//
HotKey.UnicodeChar = CHAR_NULL;
HotKey.ScanCode = SCAN_F9;
FormBrowserEx->RegisterHotKey (&HotKey, 0, 0, NULL);
HotKey.ScanCode = SCAN_F10;
FormBrowserEx->RegisterHotKey (&HotKey, 0, 0, NULL);
//
// Register the default HotKey F9 and F10 again.
//
HotKey.ScanCode = SCAN_F10;
NewString = HiiGetString (mPrivateData->HiiHandle[0], STRING_TOKEN (FUNCTION_TEN_STRING), NULL);
ASSERT (NewString != NULL);
FormBrowserEx->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString);
HotKey.ScanCode = SCAN_F9;
NewString = HiiGetString (mPrivateData->HiiHandle[0], STRING_TOKEN (FUNCTION_NINE_STRING), NULL);
ASSERT (NewString != NULL);
FormBrowserEx->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString);
}
7. 函数的最后会调用显示函数,不过这个其实没有运行,实际上运行的时候也可能因为Protocol不齐而无法运行,这个界面后续会在Setup界面里面显示,所以这个代码不管也不要紧:
//
// Example of how to display only the item we sent to HII
// When this driver is not built into Flash device image,
// it need to call SendForm to show front page by itself.
//
if (DISPLAY_ONLY_MY_ITEM <= 1) {
//
// Have the browser pull out our copy of the data, and only display our data
//
Status = FormBrowser2->SendForm (
FormBrowser2,
&(HiiHandle[DISPLAY_ONLY_MY_ITEM]),
1,
NULL,
0,
NULL,
NULL
);
HiiRemovePackages (HiiHandle[0]);
HiiRemovePackages (HiiHandle[1]);
}
以上就是初始化的过程。
前面已经说过DriverSampleDxe.inf有两个界面,其中一个Inventory.vfr比较简单,它就是一个用于显示的界面,几乎没有交互操作,所以本节主要以Vfr.vfr为主进行介绍。
关于UEFI的界面操作,实际上最重要的即使实现EFI_HII_CONFIG_ACCESS_PROTOCOL这个Protocol,它包含三个接口:
///
/// This protocol provides a callable interface between the HII and
/// drivers. Only drivers which provide IFR data to HII are required
/// to publish this protocol.
///
struct _EFI_HII_CONFIG_ACCESS_PROTOCOL {
EFI_HII_ACCESS_EXTRACT_CONFIG ExtractConfig;
EFI_HII_ACCESS_ROUTE_CONFIG RouteConfig;
EFI_HII_ACCESS_FORM_CALLBACK Callback;
} ;
第一个接口是用来获取当前的配置的,第二个接口用来处理配置的修改,而第三个函数用来处理forms browser调用来响应用户的操作。
下面分别来介绍一些这三个接口,已经它们在DriverSampleDxe.inf中的实现。
该函数原型如下:
/**
This function allows a caller to extract the current configuration for one
or more named elements from the target driver.
@param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param Request A null-terminated Unicode string in
format.
@param Progress On return, points to a character in the Request
string. Points to the string's null terminator if
request was successful. Points to the most recent
'&' before the first failing name/value pair (or
the beginning of the string if the failure is in
the first name/value pair) if the request was not
successful.
@param Results A null-terminated Unicode string in
format which has all values filled
in for the names in the Request string. String to
be allocated by the called function.
@retval EFI_SUCCESS The Results is filled with the requested values.
@retval EFI_OUT_OF_RESOURCES Not enough memory to store the results.
@retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name.
@retval EFI_NOT_FOUND Routing data doesn't match any storage in this
driver.
**/
EFI_STATUS
EFIAPI
ExtractConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Request,
OUT EFI_STRING *Progress,
OUT EFI_STRING *Results
)
第一个参数是Protocol本身,没有什么好说的。
下面的三个参数都是String类型的,而且是遵循特定格式的字符串。
在《UEFI Spec》中Request的说明如下:
可以看到它满足一种称为ConfigRequest的结构,它是怎么样的结构呢?下面是基本的元素表示:
上述有一层层的嵌套关系,到最终可以看到的大致上是有如下的形式:
GUID=…&PATH=…&Fred&George&Ron&Neville
这里比较重要的就是这个GUID=...&NAME=...&PATH=...,它标识了真正当前模块,这也是为什么在初始化的时候要安装Device Path Protocol和GUID(如下图的mHiiVendorDevicePath0和gDriverSampleFormSetGuid):
Status = gBS->InstallMultipleProtocolInterfaces (
&DriverHandle[0],
&gEfiDevicePathProtocolGuid,
&mHiiVendorDevicePath0,
&gEfiHiiConfigAccessProtocolGuid,
&mPrivateData->ConfigAccess,
NULL
);
ASSERT_EFI_ERROR (Status);
mPrivateData->DriverHandle[0] = DriverHandle[0];
//
// Publish our HII data
//
HiiHandle[0] = HiiAddPackages (
&gDriverSampleFormSetGuid,
DriverHandle[0],
DriverSampleStrings,
VfrBin,
NULL
);
关于Results的说明如下:
关于MultiConfigAltResp的形式在前面的途中也已经有表示了,它是相对于Request的一个返回,还是以上面的字符串为例,则这里会返回的可能值如下:
GUID=…&PATH=…&Fred=16&George=16&Ron=12&Neville=11&GUID=…&PATH=…&ALTCFG=0037&Fred=12&Neville=7
Process是指向Request的指针,它指向的是还没有对应Results返回的那一个Request,是用来作为进度的指示。当它指向NULL的时候就表示所有的Request都有了对应的Result。
下面简单说明DriverSampleDxe.inf模块中该函数的基本操作:
1. 首先是获取Setup变量:
//
// Get Buffer Storage data from EFI variable.
// Try to get the current setting from variable.
//
BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION);
Status = gRT->GetVariable (
VariableName,
&gDriverSampleFormSetGuid,
NULL,
&BufferSize,
&PrivateData->Configuration
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
因为这个函数的作用就是获取配置,而这个配置通常就是存放在变量中的。
2. 构造Request字符串,这里有两种情况。
2.1 第一种是传入的参数Request==NULL的情况,此时表示需要所有的配置,下面是这种情况下穿件的Request字符串:
//
// Request is set to NULL, construct full request string.
//
//
// Allocate and fill a buffer large enough to hold the template
// followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
//
ConfigRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, VariableName, PrivateData->DriverHandle[0]);
Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
ConfigRequest = AllocateZeroPool (Size);
ASSERT (ConfigRequest != NULL);
AllocatedRequest = TRUE;
UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
FreePool (ConfigRequestHdr);
ConfigRequestHdr = NULL;
这个Request字符串分为两个部分,一部分通过函数HiiConstructConfigHdr来创建,它创建的是Request的头部,格式如下:
GUID=32&NAME=NameLength&PATH=DevicePathSize
再稍微细看一下HiiConstructConfigHdr()这个函数,它接受三个参数,第一个是FormSet对应的GUID,第二个是变量的名称,第三个是Device Path所在的Handle,它们相应的就对应到了Request字符串的三个部分GUID=、NAME=和PATH=。如下是该函数得到的结果:
GUID=f4274aa000df424db55239511302113d&NAME=004d0079004900660072004e00560044006100740061&PATH=01041400f4274aa000df424db55239511302113d7fff0400
上述显示是通过DEBUG打印出来的,看上去与格式有一些出入,不过应该也能说明问题。
另外一部分是格式如下:
&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW
这一部分就不需要多讲了,实际上就是一个UnicodeSPrint的调用。
最终得到的Request字符串如下:
GUID=f4274aa000df424db55239511302113d&NAME=004d0079004900660072004e00560044006100740061&PATH=01041400f4274aa000df424db55239511302113d7fff0400&OFFSET=0&WIDTH=00000000000000CF
2.2 第二种情况是Request参数!=NULL的情况,这表示调用者需要某个特定的配置。
首先会对Request参数进行有效性判断:
//
// Check routing data in .
// Note: if only one Storage is used, then this checking could be skipped.
//
if (!HiiIsConfigHdrMatch (Request, &gDriverSampleFormSetGuid, NULL)) {
return EFI_NOT_FOUND;
}
//
// Check whether request for EFI Varstore. EFI varstore get data
// through hii database, not support in this path.
//
if (HiiIsConfigHdrMatch(Request, &gDriverSampleFormSetGuid, MyEfiVar)) {
return EFI_UNSUPPORTED;
}
注意第二个判断条件,有一种变量不会在本函数处理。在Vfr.vfr中可以看到存在三种变量:
//
// Define a Buffer Storage (EFI_IFR_VARSTORE)
//
varstore DRIVER_SAMPLE_CONFIGURATION, // This is the data structure type
varid = CONFIGURATION_VARSTORE_ID, // Optional VarStore ID
name = MyIfrNVData, // Define referenced name in vfr
guid = DRIVER_SAMPLE_FORMSET_GUID; // GUID of this buffer storage
//
// Define a EFI variable Storage (EFI_IFR_VARSTORE_EFI)
//
efivarstore MY_EFI_VARSTORE_DATA,
attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, // EFI variable attribures
name = MyEfiVar,
guid = DRIVER_SAMPLE_FORMSET_GUID;
//
// Define a Name/Value Storage (EFI_IFR_VARSTORE_NAME_VALUE)
//
namevaluevarstore MyNameValueVar, // Define storage reference name in vfr
name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME0), // Define Name list of this storage, refer it by MyNameValueVar[0]
name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME1), // Define Name list of this storage, refer it by MyNameValueVar[1]
name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME2), // Define Name list of this storage, refer it by MyNameValueVar[2]
guid = DRIVER_SAMPLE_FORMSET_GUID; // GUID of this Name/Value storage
在这里能够处理的是第一种和最后一种,中间的efivarstore不是在这里处理的。
之后会判断这个Request参数是否包含OFFSET=,如果没有的话就会添加默认的:
if (StrStr (Request, L"OFFSET") == NULL) {
//
// Check Request Element does exist in Reques String
//
StrPointer = StrStr (Request, L"PATH");
if (StrPointer == NULL) {
return EFI_INVALID_PARAMETER;
}
if (StrStr (StrPointer, L"&") == NULL) {
Size = (StrLen (Request) + 32 + 1) * sizeof (CHAR16);
ConfigRequest = AllocateZeroPool (Size);
ASSERT (ConfigRequest != NULL);
AllocatedRequest = TRUE;
UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", Request, (UINT64)BufferSize);
}
}
第2步的最后将得到一个完整的Request字符串ConfigRequest。
3. ConfigRequest也有两种情况,一种是有包含OFFSET=的,另一种是没有包含OFFSET=的字符串,前者表示要处理的是Name/Value类型的变量,后者表示要处理的普通的变量,具体可以参看前面2.2中的说明。
3.1 对于处理Name/Value类型的变量的变量代码如下:
//
// Allocate memory for , e.g. Name0=0x11, Name1=0x1234, Name2="ABCD"
// ::=&Name0&Name1&Name2
// ::=&Name0=11&Name1=1234&Name2=0041004200430044
//
BufferSize = (StrLen (ConfigRequest) +
1 + sizeof (PrivateData->Configuration.NameValueVar0) * 2 +
1 + sizeof (PrivateData->Configuration.NameValueVar1) * 2 +
1 + sizeof (PrivateData->Configuration.NameValueVar2) * 2 + 1) * sizeof (CHAR16);
*Results = AllocateZeroPool (BufferSize);
ASSERT (*Results != NULL);
StrCpyS (*Results, BufferSize / sizeof (CHAR16), ConfigRequest);
Value = *Results;
//
// Append value of NameValueVar0, type is UINT8
//
if ((Value = StrStr (*Results, PrivateData->NameValueName[0])) != NULL) {
Value += StrLen (PrivateData->NameValueName[0]);
ValueStrLen = ((sizeof (PrivateData->Configuration.NameValueVar0) * 2) + 1);
CopyMem (Value + ValueStrLen, Value, StrSize (Value));
BackupChar = Value[ValueStrLen];
*Value++ = L'=';
UnicodeValueToStringS (
Value,
BufferSize - ((UINTN)Value - (UINTN)*Results),
PREFIX_ZERO | RADIX_HEX,
PrivateData->Configuration.NameValueVar0,
sizeof (PrivateData->Configuration.NameValueVar0) * 2
);
Value += StrnLenS (Value, (BufferSize - ((UINTN)Value - (UINTN)*Results)) / sizeof (CHAR16));
*Value = BackupChar;
}
其实也没有特别需要说明的。它们处理的是mPrivateData的如下部分:
//
// Name/Value storage Name list
//
EFI_STRING_ID NameStringId[NAME_VALUE_NAME_NUMBER];
EFI_STRING NameValueName[NAME_VALUE_NAME_NUMBER];
3.2 普通变脸处理的是mPrivateData的如下内容:
DRIVER_SAMPLE_CONFIGURATION Configuration;
对应的函数如下:
//
// Convert buffer data to by helper function BlockToConfig()
//
Status = HiiConfigRouting->BlockToConfig (
HiiConfigRouting,
ConfigRequest,
(UINT8 *) &PrivateData->Configuration,
BufferSize,
Results,
Progress
);
这个UEFI Setup框架提供的基本函数了,这里不关注它的具体实现。
以上就是具体的处理函数了,之后会返回结果到Results入参中。
4. 最后就是一些收尾工作:
//
// Set Progress string to the original request string.
//
if (Request == NULL) {
*Progress = NULL;
} else if (StrStr (Request, L"OFFSET") == NULL) {
*Progress = Request + StrLen (Request);
}
以上是ExtractConfig()函数的基本流程。
该函数原型如下:
/**
This function processes the results of changes in configuration.
@param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param Configuration A null-terminated Unicode string in
format.
@param Progress A pointer to a string filled in with the offset of
the most recent '&' before the first failing
name/value pair (or the beginning of the string if
the failure is in the first name/value pair) or
the terminating NULL if all was successful.
@retval EFI_SUCCESS The Results is processed successfully.
@retval EFI_INVALID_PARAMETER Configuration is NULL.
@retval EFI_NOT_FOUND Routing data doesn't match any storage in this
driver.
**/
EFI_STATUS
EFIAPI
RouteConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Configuration,
OUT EFI_STRING *Progress
)
该函数主要做的是存储的动作,下面是基本的流程:
1. 一些初始化和入参判断:
if (Configuration == NULL || Progress == NULL) {
return EFI_INVALID_PARAMETER;
}
PrivateData = DRIVER_SAMPLE_PRIVATE_FROM_THIS (This);
HiiConfigRouting = PrivateData->HiiConfigRouting;
*Progress = Configuration;
//
// Check routing data in .
// Note: if only one Storage is used, then this checking could be skipped.
//
if (!HiiIsConfigHdrMatch (Configuration, &gDriverSampleFormSetGuid, NULL)) {
return EFI_NOT_FOUND;
}
//
// Check whether request for EFI Varstore. EFI varstore get data
// through hii database, not support in this path.
//
if (HiiIsConfigHdrMatch(Configuration, &gDriverSampleFormSetGuid, MyEfiVar)) {
return EFI_UNSUPPORTED;
}
//
// Get Buffer Storage data from EFI variable
//
BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION);
Status = gRT->GetVariable (
VariableName,
&gDriverSampleFormSetGuid,
NULL,
&BufferSize,
&PrivateData->Configuration
);
if (EFI_ERROR (Status)) {
return Status;
}
没什么好多说的。
2. 下面又分为两个部分,用来处理两种类似的变量,前面已经介绍过,efivarstore不在这里处理,这里要处理的是namevaluevarstore和varstore。
2.1 先介绍varstore,它的存储很简单,直接调用现有接口:
//
// Convert to buffer data by helper function ConfigToBlock()
//
BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION);
Status = HiiConfigRouting->ConfigToBlock (
HiiConfigRouting,
Configuration,
(UINT8 *) &PrivateData->Configuration,
&BufferSize,
Progress
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Store Buffer Storage back to EFI variable
//
Status = gRT->SetVariable(
VariableName,
&gDriverSampleFormSetGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (DRIVER_SAMPLE_CONFIGURATION),
&PrivateData->Configuration
);
2.2 再回到前一种namevaluevarstore,这种配置中,入参Configuration字符串中不带OFFSET=:
if (StrStr (Configuration, L"OFFSET") == NULL) {
//
// Update Name/Value storage Names
//
Status = LoadNameValueNames (PrivateData);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Convert value for NameValueVar0
//
if ((Value = StrStr (Configuration, PrivateData->NameValueName[0])) != NULL) {
//
// Skip "Name="
//
Value += StrLen (PrivateData->NameValueName[0]);
Value++;
//
// Get Value String
//
StrPtr = StrStr (Value, L"&");
if (StrPtr == NULL) {
StrPtr = Value + StrLen (Value);
}
//
// Convert Value to Buffer data
//
DataBuffer = (UINT8 *) &PrivateData->Configuration.NameValueVar0;
ZeroMem (TemStr, sizeof (TemStr));
for (Index = 0, StrPtr --; StrPtr >= Value; StrPtr --, Index ++) {
TemStr[0] = *StrPtr;
DigitUint8 = (UINT8) StrHexToUint64 (TemStr);
if ((Index & 1) == 0) {
DataBuffer [Index/2] = DigitUint8;
} else {
DataBuffer [Index/2] = (UINT8) ((UINT8) (DigitUint8 << 4) + DataBuffer [Index/2]);
}
}
}
后面两个省略。最后还是要保存变量:
//
// Store Buffer Storage back to EFI variable
//
Status = gRT->SetVariable(
VariableName,
&gDriverSampleFormSetGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (DRIVER_SAMPLE_CONFIGURATION),
&PrivateData->Configuration
);
该函数原型如下:
/**
This function processes the results of changes in configuration.
@param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param Action Specifies the type of action taken by the browser.
@param QuestionId A unique value which is sent to the original
exporting driver so that it can identify the type
of data to expect.
@param Type The type of value for the question.
@param Value A pointer to the data being sent to the original
exporting driver.
@param ActionRequest On return, points to the action requested by the
callback function.
@retval EFI_SUCCESS The callback successfully handled the action.
@retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
variable and its data.
@retval EFI_DEVICE_ERROR The variable could not be saved.
@retval EFI_UNSUPPORTED The specified Action is not supported by the
callback.
**/
EFI_STATUS
EFIAPI
DriverCallback (
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
)
这个函数首先要说明下它的各个参数:
This:这个不多说。
Action:它是一个整型,表示的是一个动作,有如下的可能:
#define EFI_BROWSER_ACTION_CHANGING 0
#define EFI_BROWSER_ACTION_CHANGED 1
#define EFI_BROWSER_ACTION_RETRIEVE 2
#define EFI_BROWSER_ACTION_FORM_OPEN 3
#define EFI_BROWSER_ACTION_FORM_CLOSE 4
#define EFI_BROWSER_ACTION_SUBMITTED 5
#define EFI_BROWSER_ACTION_DEFAULT_STANDARD 0x1000
#define EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING 0x1001
#define EFI_BROWSER_ACTION_DEFAULT_SAFE 0x1002
#define EFI_BROWSER_ACTION_DEFAULT_PLATFORM 0x2000
#define EFI_BROWSER_ACTION_DEFAULT_HARDWARE 0x3000
#define EFI_BROWSER_ACTION_DEFAULT_FIRMWARE 0x4000
QuestionId:它是一个16位的整型,对应到Vfr.vfr中的key这个元素,下面是一个例子:
help = STRING_TOKEN(STR_EXIT_TEXT),
text = STRING_TOKEN(STR_EXIT_TEXT),
flags = INTERACTIVE, // VfrCompiler will generate opcode EFI_IFR_ACTION for Text marked as INTERACTIVE
key = 0x1245;
text
help = STRING_TOKEN(STR_SAVE_TEXT),
text = STRING_TOKEN(STR_SAVE_TEXT),
flags = INTERACTIVE, // VfrCompiler will generate opcode EFI_IFR_ACTION for Text marked as INTERACTIVE
key = 0x1246;
Type:表示的是对应VFR中的元素的类型,下面是具体的可能值:
//
// Types of the option's value.
//
#define EFI_IFR_TYPE_NUM_SIZE_8 0x00
#define EFI_IFR_TYPE_NUM_SIZE_16 0x01
#define EFI_IFR_TYPE_NUM_SIZE_32 0x02
#define EFI_IFR_TYPE_NUM_SIZE_64 0x03
#define EFI_IFR_TYPE_BOOLEAN 0x04
#define EFI_IFR_TYPE_TIME 0x05
#define EFI_IFR_TYPE_DATE 0x06
#define EFI_IFR_TYPE_STRING 0x07
#define EFI_IFR_TYPE_OTHER 0x08
#define EFI_IFR_TYPE_UNDEFINED 0x09
#define EFI_IFR_TYPE_ACTION 0x0A
#define EFI_IFR_TYPE_BUFFER 0x0B
#define EFI_IFR_TYPE_REF 0x0C
Value:它是一个结构图如下所示:
typedef union {
UINT8 u8;
UINT16 u16;
UINT32 u32;
UINT64 u64;
BOOLEAN b;
EFI_HII_TIME time;
EFI_HII_DATE date;
EFI_STRING_ID string; ///< EFI_IFR_TYPE_STRING, EFI_IFR_TYPE_ACTION
EFI_HII_REF ref; ///< EFI_IFR_TYPE_REF
// UINT8 buffer[]; ///< EFI_IFR_TYPE_BUFFER
} EFI_IFR_TYPE_VALUE;
要根据具体的情况使用。
ActionRequest:它是一个整型,有如下的取值:
#define EFI_BROWSER_ACTION_REQUEST_NONE 0
#define EFI_BROWSER_ACTION_REQUEST_RESET 1
#define EFI_BROWSER_ACTION_REQUEST_SUBMIT 2
#define EFI_BROWSER_ACTION_REQUEST_EXIT 3
#define EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT 4
#define EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT 5
#define EFI_BROWSER_ACTION_REQUEST_FORM_APPLY 6
#define EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD 7
#define EFI_BROWSER_ACTION_REQUEST_RECONNECT 8
表示操作结束之后要对Setup做什么动作。
本函数根据入参Action的值有很多不同的操作,这里就不再具体说明。