/** @file | |
Entry and initialization module for the browser. | |
Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "Setup.h" | |
SETUP_DRIVER_PRIVATE_DATA mPrivateData = { | |
SETUP_DRIVER_SIGNATURE, | |
NULL, | |
{ | |
SendForm, | |
BrowserCallback | |
}, | |
{ | |
SetScope, | |
RegisterHotKey, | |
RegiserExitHandler, | |
SaveReminder | |
}, | |
{ | |
BROWSER_EXTENSION2_VERSION_1_1, | |
SetScope, | |
RegisterHotKey, | |
RegiserExitHandler, | |
IsBrowserDataModified, | |
ExecuteAction, | |
{NULL,NULL}, | |
{NULL,NULL}, | |
IsResetRequired | |
} | |
}; | |
EFI_HII_DATABASE_PROTOCOL *mHiiDatabase; | |
EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting; | |
EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *mPathFromText; | |
EDKII_FORM_DISPLAY_ENGINE_PROTOCOL *mFormDisplay; | |
UINTN gBrowserContextCount = 0; | |
LIST_ENTRY gBrowserContextList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserContextList); | |
LIST_ENTRY gBrowserFormSetList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserFormSetList); | |
LIST_ENTRY gBrowserHotKeyList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserHotKeyList); | |
LIST_ENTRY gBrowserStorageList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserStorageList); | |
LIST_ENTRY gBrowserSaveFailFormSetList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserSaveFailFormSetList); | |
BOOLEAN mSystemSubmit = FALSE; | |
BOOLEAN gResetRequired; | |
BOOLEAN gExitRequired; | |
BROWSER_SETTING_SCOPE gBrowserSettingScope = FormSetLevel; | |
BOOLEAN mBrowserScopeFirstSet = TRUE; | |
EXIT_HANDLER ExitHandlerFunction = NULL; | |
FORM_BROWSER_FORMSET *mSystemLevelFormSet; | |
// | |
// Browser Global Strings | |
// | |
CHAR16 *gEmptyString; | |
CHAR16 *mUnknownString = L"!"; | |
EFI_GUID gZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; | |
extern EFI_GUID mCurrentFormSetGuid; | |
extern EFI_HII_HANDLE mCurrentHiiHandle; | |
extern UINT16 mCurrentFormId; | |
extern FORM_DISPLAY_ENGINE_FORM gDisplayFormData; | |
/** | |
Create a menu with specified formset GUID and form ID, and add it as a child | |
of the given parent menu. | |
@param HiiHandle Hii handle related to this formset. | |
@param FormSetGuid The Formset Guid of menu to be added. | |
@param FormId The Form ID of menu to be added. | |
@param QuestionId The question id of this menu to be added. | |
@return A pointer to the newly added menu or NULL if memory is insufficient. | |
**/ | |
FORM_ENTRY_INFO * | |
UiAddMenuList ( | |
IN EFI_HII_HANDLE HiiHandle, | |
IN EFI_GUID *FormSetGuid, | |
IN UINT16 FormId, | |
IN UINT16 QuestionId | |
) | |
{ | |
FORM_ENTRY_INFO *MenuList; | |
MenuList = AllocateZeroPool (sizeof (FORM_ENTRY_INFO)); | |
if (MenuList == NULL) { | |
return NULL; | |
} | |
MenuList->Signature = FORM_ENTRY_INFO_SIGNATURE; | |
MenuList->HiiHandle = HiiHandle; | |
CopyMem (&MenuList->FormSetGuid, FormSetGuid, sizeof (EFI_GUID)); | |
MenuList->FormId = FormId; | |
MenuList->QuestionId = QuestionId; | |
// | |
// If parent is not specified, it is the root Form of a Formset | |
// | |
InsertTailList (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &MenuList->Link); | |
return MenuList; | |
} | |
/** | |
Return the form id for the input hiihandle and formset. | |
@param HiiHandle HiiHandle for FormSet. | |
@param FormSetGuid The Formset GUID of the menu to search. | |
@return First form's id for this form set. | |
**/ | |
EFI_FORM_ID | |
GetFirstFormId ( | |
IN EFI_HII_HANDLE HiiHandle, | |
IN EFI_GUID *FormSetGuid | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_BROWSER_FORM *Form; | |
Link = GetFirstNode (&gCurrentSelection->FormSet->FormListHead); | |
Form = FORM_BROWSER_FORM_FROM_LINK (Link); | |
return Form->FormId; | |
} | |
/** | |
Search Menu with given FormSetGuid and FormId in all cached menu list. | |
@param HiiHandle HiiHandle for FormSet. | |
@param FormSetGuid The Formset GUID of the menu to search. | |
@param FormId The Form ID of menu to search. | |
@return A pointer to menu found or NULL if not found. | |
**/ | |
FORM_ENTRY_INFO * | |
UiFindMenuList ( | |
IN EFI_HII_HANDLE HiiHandle, | |
IN EFI_GUID *FormSetGuid, | |
IN UINT16 FormId | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_ENTRY_INFO *MenuList; | |
FORM_ENTRY_INFO *RetMenu; | |
EFI_FORM_ID FirstFormId; | |
RetMenu = NULL; | |
Link = GetFirstNode (&mPrivateData.FormBrowserEx2.FormViewHistoryHead); | |
while (!IsNull (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, Link)) { | |
MenuList = FORM_ENTRY_INFO_FROM_LINK (Link); | |
Link = GetNextNode (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, Link); | |
// | |
// If already find the menu, free the menus behind it. | |
// | |
if (RetMenu != NULL) { | |
RemoveEntryList (&MenuList->Link); | |
FreePool (MenuList); | |
continue; | |
} | |
// | |
// Find the same FromSet. | |
// | |
if (MenuList->HiiHandle == HiiHandle) { | |
if (CompareGuid (&MenuList->FormSetGuid, &gZeroGuid)) { | |
// | |
// FormSetGuid is not specified. | |
// | |
RetMenu = MenuList; | |
} else if (CompareGuid (&MenuList->FormSetGuid, FormSetGuid)) { | |
if (MenuList->FormId == FormId) { | |
RetMenu = MenuList; | |
} else if (FormId == 0 || MenuList->FormId == 0 ) { | |
FirstFormId = GetFirstFormId (HiiHandle, FormSetGuid); | |
if ((FormId == 0 && FirstFormId == MenuList->FormId) || (MenuList->FormId ==0 && FirstFormId == FormId)) { | |
RetMenu = MenuList; | |
} | |
} | |
} | |
} | |
} | |
return RetMenu; | |
} | |
/** | |
Find parent menu for current menu. | |
@param CurrentMenu Current Menu | |
@param SettingLevel Whether find parent menu in Form Level or Formset level. | |
In form level, just find the parent menu; | |
In formset level, find the parent menu which has different | |
formset guid value. | |
@retval The parent menu for current menu. | |
**/ | |
FORM_ENTRY_INFO * | |
UiFindParentMenu ( | |
IN FORM_ENTRY_INFO *CurrentMenu, | |
IN BROWSER_SETTING_SCOPE SettingLevel | |
) | |
{ | |
FORM_ENTRY_INFO *ParentMenu; | |
LIST_ENTRY *Link; | |
ASSERT (SettingLevel == FormLevel || SettingLevel == FormSetLevel); | |
if (CurrentMenu == NULL) { | |
return NULL; | |
} | |
ParentMenu = NULL; | |
Link = &CurrentMenu->Link; | |
while (Link->BackLink != &mPrivateData.FormBrowserEx2.FormViewHistoryHead) { | |
ParentMenu = FORM_ENTRY_INFO_FROM_LINK (Link->BackLink); | |
if (SettingLevel == FormLevel) { | |
// | |
// For FormLevel, just find the parent menu, return. | |
// | |
break; | |
} | |
if (!CompareGuid (&CurrentMenu->FormSetGuid, &ParentMenu->FormSetGuid)) { | |
// | |
// For SystemLevel, must find the menu which has different formset. | |
// | |
break; | |
} | |
Link = Link->BackLink; | |
} | |
// | |
// Not find the parent menu, just return NULL. | |
// | |
if (Link->BackLink == &mPrivateData.FormBrowserEx2.FormViewHistoryHead) { | |
return NULL; | |
} | |
return ParentMenu; | |
} | |
/** | |
Free Menu list linked list. | |
@param MenuListHead One Menu list point in the menu list. | |
**/ | |
VOID | |
UiFreeMenuList ( | |
LIST_ENTRY *MenuListHead | |
) | |
{ | |
FORM_ENTRY_INFO *MenuList; | |
while (!IsListEmpty (MenuListHead)) { | |
MenuList = FORM_ENTRY_INFO_FROM_LINK (MenuListHead->ForwardLink); | |
RemoveEntryList (&MenuList->Link); | |
FreePool (MenuList); | |
} | |
} | |
/** | |
Copy current Menu list to the new menu list. | |
@param NewMenuListHead New create Menu list. | |
@param CurrentMenuListHead Current Menu list. | |
**/ | |
VOID | |
UiCopyMenuList ( | |
OUT LIST_ENTRY *NewMenuListHead, | |
IN LIST_ENTRY *CurrentMenuListHead | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_ENTRY_INFO *MenuList; | |
FORM_ENTRY_INFO *NewMenuEntry; | |
// | |
// If new menu list not empty, free it first. | |
// | |
UiFreeMenuList (NewMenuListHead); | |
Link = GetFirstNode (CurrentMenuListHead); | |
while (!IsNull (CurrentMenuListHead, Link)) { | |
MenuList = FORM_ENTRY_INFO_FROM_LINK (Link); | |
Link = GetNextNode (CurrentMenuListHead, Link); | |
NewMenuEntry = AllocateZeroPool (sizeof (FORM_ENTRY_INFO)); | |
ASSERT (NewMenuEntry != NULL); | |
NewMenuEntry->Signature = FORM_ENTRY_INFO_SIGNATURE; | |
NewMenuEntry->HiiHandle = MenuList->HiiHandle; | |
CopyMem (&NewMenuEntry->FormSetGuid, &MenuList->FormSetGuid, sizeof (EFI_GUID)); | |
NewMenuEntry->FormId = MenuList->FormId; | |
NewMenuEntry->QuestionId = MenuList->QuestionId; | |
InsertTailList (NewMenuListHead, &NewMenuEntry->Link); | |
} | |
} | |
/** | |
Load all hii formset to the browser. | |
**/ | |
VOID | |
LoadAllHiiFormset ( | |
VOID | |
) | |
{ | |
FORM_BROWSER_FORMSET *LocalFormSet; | |
EFI_HII_HANDLE *HiiHandles; | |
UINTN Index; | |
EFI_GUID ZeroGuid; | |
EFI_STATUS Status; | |
FORM_BROWSER_FORMSET *OldFormset; | |
OldFormset = mSystemLevelFormSet; | |
// | |
// Get all the Hii handles | |
// | |
HiiHandles = HiiGetHiiHandles (NULL); | |
ASSERT (HiiHandles != NULL); | |
// | |
// Search for formset of each class type | |
// | |
for (Index = 0; HiiHandles[Index] != NULL; Index++) { | |
// | |
// Check HiiHandles[Index] does exist in global maintain list. | |
// | |
if (GetFormSetFromHiiHandle (HiiHandles[Index]) != NULL) { | |
continue; | |
} | |
// | |
// Initilize FormSet Setting | |
// | |
LocalFormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); | |
ASSERT (LocalFormSet != NULL); | |
mSystemLevelFormSet = LocalFormSet; | |
ZeroMem (&ZeroGuid, sizeof (ZeroGuid)); | |
Status = InitializeFormSet (HiiHandles[Index], &ZeroGuid, LocalFormSet); | |
if (EFI_ERROR (Status) || IsListEmpty (&LocalFormSet->FormListHead)) { | |
DestroyFormSet (LocalFormSet); | |
continue; | |
} | |
InitializeCurrentSetting (LocalFormSet); | |
// | |
// Initilize Questions' Value | |
// | |
Status = LoadFormSetConfig (NULL, LocalFormSet); | |
if (EFI_ERROR (Status)) { | |
DestroyFormSet (LocalFormSet); | |
continue; | |
} | |
} | |
// | |
// Free resources, and restore gOldFormSet and gClassOfVfr | |
// | |
FreePool (HiiHandles); | |
mSystemLevelFormSet = OldFormset; | |
} | |
/** | |
Pop up the error info. | |
@param BrowserStatus The input browser status. | |
@param HiiHandle The Hiihandle for this opcode. | |
@param OpCode The opcode use to get the erro info and timeout value. | |
@param ErrorString Error string used by BROWSER_NO_SUBMIT_IF. | |
**/ | |
UINT32 | |
PopupErrorMessage ( | |
IN UINT32 BrowserStatus, | |
IN EFI_HII_HANDLE HiiHandle, | |
IN EFI_IFR_OP_HEADER *OpCode, OPTIONAL | |
IN CHAR16 *ErrorString | |
) | |
{ | |
FORM_DISPLAY_ENGINE_STATEMENT *Statement; | |
USER_INPUT UserInputData; | |
Statement = NULL; | |
if (OpCode != NULL) { | |
Statement = AllocateZeroPool (sizeof(FORM_DISPLAY_ENGINE_STATEMENT)); | |
ASSERT (Statement != NULL); | |
Statement->OpCode = OpCode; | |
gDisplayFormData.HighLightedStatement = Statement; | |
} | |
// | |
// Used to compatible with old display engine. | |
// New display engine not use this field. | |
// | |
gDisplayFormData.ErrorString = ErrorString; | |
gDisplayFormData.BrowserStatus = BrowserStatus; | |
if (HiiHandle != NULL) { | |
gDisplayFormData.HiiHandle = HiiHandle; | |
} | |
mFormDisplay->FormDisplay (&gDisplayFormData, &UserInputData); | |
gDisplayFormData.BrowserStatus = BROWSER_SUCCESS; | |
gDisplayFormData.ErrorString = NULL; | |
if (OpCode != NULL) { | |
FreePool (Statement); | |
} | |
return UserInputData.Action; | |
} | |
/** | |
This is the routine which an external caller uses to direct the browser | |
where to obtain it's information. | |
@param This The Form Browser protocol instanse. | |
@param Handles A pointer to an array of Handles. If HandleCount > 1 we | |
display a list of the formsets for the handles specified. | |
@param HandleCount The number of Handles specified in Handle. | |
@param FormSetGuid This field points to the EFI_GUID which must match the Guid | |
field in the EFI_IFR_FORM_SET op-code for the specified | |
forms-based package. If FormSetGuid is NULL, then this | |
function will display the first found forms package. | |
@param FormId This field specifies which EFI_IFR_FORM to render as the first | |
displayable page. If this field has a value of 0x0000, then | |
the forms browser will render the specified forms in their encoded order. | |
@param ScreenDimensions Points to recommended form dimensions, including any non-content area, in | |
characters. | |
@param ActionRequest Points to the action recommended by the form. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. | |
@retval EFI_NOT_FOUND No valid forms could be found to display. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SendForm ( | |
IN CONST EFI_FORM_BROWSER2_PROTOCOL *This, | |
IN EFI_HII_HANDLE *Handles, | |
IN UINTN HandleCount, | |
IN EFI_GUID *FormSetGuid, OPTIONAL | |
IN UINT16 FormId, OPTIONAL | |
IN CONST EFI_SCREEN_DESCRIPTOR *ScreenDimensions, OPTIONAL | |
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
UI_MENU_SELECTION *Selection; | |
UINTN Index; | |
FORM_BROWSER_FORMSET *FormSet; | |
FORM_ENTRY_INFO *MenuList; | |
// | |
// If EDKII_FORM_DISPLAY_ENGINE_PROTOCOL not found, return EFI_UNSUPPORTED. | |
// | |
if (mFormDisplay == NULL) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Save globals used by SendForm() | |
// | |
SaveBrowserContext (); | |
gResetRequired = FALSE; | |
gExitRequired = FALSE; | |
Status = EFI_SUCCESS; | |
gEmptyString = L""; | |
gDisplayFormData.ScreenDimensions = (EFI_SCREEN_DESCRIPTOR *) ScreenDimensions; | |
for (Index = 0; Index < HandleCount; Index++) { | |
Selection = AllocateZeroPool (sizeof (UI_MENU_SELECTION)); | |
ASSERT (Selection != NULL); | |
Selection->Handle = Handles[Index]; | |
if (FormSetGuid != NULL) { | |
CopyMem (&Selection->FormSetGuid, FormSetGuid, sizeof (EFI_GUID)); | |
Selection->FormId = FormId; | |
} else { | |
CopyMem (&Selection->FormSetGuid, &gEfiHiiPlatformSetupFormsetGuid, sizeof (EFI_GUID)); | |
} | |
do { | |
FormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); | |
ASSERT (FormSet != NULL); | |
// | |
// Validate the HiiHandle | |
// if validate failed, find the first validate parent HiiHandle. | |
// | |
if (!ValidateHiiHandle(Selection->Handle)) { | |
FindNextMenu (Selection, FormSetLevel); | |
} | |
// | |
// Initialize internal data structures of FormSet | |
// | |
Status = InitializeFormSet (Selection->Handle, &Selection->FormSetGuid, FormSet); | |
if (EFI_ERROR (Status) || IsListEmpty (&FormSet->FormListHead)) { | |
DestroyFormSet (FormSet); | |
break; | |
} | |
Selection->FormSet = FormSet; | |
mSystemLevelFormSet = FormSet; | |
// | |
// Display this formset | |
// | |
gCurrentSelection = Selection; | |
Status = SetupBrowser (Selection); | |
gCurrentSelection = NULL; | |
mSystemLevelFormSet = NULL; | |
// | |
// If no data is changed, don't need to save current FormSet into the maintain list. | |
// | |
if (!IsNvUpdateRequiredForFormSet (FormSet)) { | |
CleanBrowserStorage(FormSet); | |
RemoveEntryList (&FormSet->Link); | |
DestroyFormSet (FormSet); | |
} | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
} while (Selection->Action == UI_ACTION_REFRESH_FORMSET); | |
FreePool (Selection); | |
} | |
if (ActionRequest != NULL) { | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; | |
if (gResetRequired) { | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_RESET; | |
} | |
} | |
mFormDisplay->ExitDisplay(); | |
// | |
// Clear the menu history data. | |
// | |
while (!IsListEmpty (&mPrivateData.FormBrowserEx2.FormViewHistoryHead)) { | |
MenuList = FORM_ENTRY_INFO_FROM_LINK (mPrivateData.FormBrowserEx2.FormViewHistoryHead.ForwardLink); | |
RemoveEntryList (&MenuList->Link); | |
FreePool (MenuList); | |
} | |
// | |
// Restore globals used by SendForm() | |
// | |
RestoreBrowserContext (); | |
return Status; | |
} | |
/** | |
Get or set data to the storage. | |
@param ResultsDataSize The size of the buffer associatedwith ResultsData. | |
@param ResultsData A string returned from an IFR browser or | |
equivalent. The results string will have no | |
routing information in them. | |
@param RetrieveData A BOOLEAN field which allows an agent to retrieve | |
(if RetrieveData = TRUE) data from the uncommitted | |
browser state information or set (if RetrieveData | |
= FALSE) data in the uncommitted browser state | |
information. | |
@param Storage The pointer to the storage. | |
@retval EFI_SUCCESS The results have been distributed or are awaiting | |
distribution. | |
**/ | |
EFI_STATUS | |
ProcessStorage ( | |
IN OUT UINTN *ResultsDataSize, | |
IN OUT EFI_STRING *ResultsData, | |
IN BOOLEAN RetrieveData, | |
IN BROWSER_STORAGE *Storage | |
) | |
{ | |
CHAR16 *ConfigResp; | |
EFI_STATUS Status; | |
CHAR16 *StrPtr; | |
UINTN BufferSize; | |
UINTN TmpSize; | |
FORMSET_STORAGE *BrowserStorage; | |
if (RetrieveData) { | |
// | |
// Generate <ConfigResp> | |
// | |
Status = StorageToConfigResp (Storage, &ConfigResp, Storage->ConfigRequest, TRUE); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Skip <ConfigHdr> and '&' to point to <ConfigBody> when first copy the configbody. | |
// Also need to consider add "\0" at first time. | |
// | |
StrPtr = StrStr (ConfigResp, L"PATH"); | |
ASSERT (StrPtr != NULL); | |
StrPtr = StrStr (StrPtr, L"&"); | |
StrPtr += 1; | |
BufferSize = StrSize (StrPtr); | |
// | |
// Copy the data if the input buffer is bigger enough. | |
// | |
if (*ResultsDataSize >= BufferSize) { | |
StrCpy (*ResultsData, StrPtr); | |
} | |
*ResultsDataSize = BufferSize; | |
FreePool (ConfigResp); | |
} else { | |
// | |
// Prepare <ConfigResp> | |
// | |
BrowserStorage = GetFstStgFromBrsStg (Storage); | |
ASSERT (BrowserStorage != NULL); | |
TmpSize = StrLen (*ResultsData); | |
BufferSize = (TmpSize + StrLen (BrowserStorage->ConfigHdr) + 2) * sizeof (CHAR16); | |
ConfigResp = AllocateZeroPool (BufferSize); | |
ASSERT (ConfigResp != NULL); | |
StrCpy (ConfigResp, BrowserStorage->ConfigHdr); | |
StrCat (ConfigResp, L"&"); | |
StrCat (ConfigResp, *ResultsData); | |
// | |
// Update Browser uncommited data | |
// | |
Status = ConfigRespToStorage (Storage, ConfigResp); | |
FreePool (ConfigResp); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This routine called this service in the browser to retrieve or set certain uncommitted | |
state information that resides in the open formsets. | |
@param This A pointer to the EFI_FORM_BROWSER2_PROTOCOL | |
instance. | |
@param ResultsDataSize A pointer to the size of the buffer associated | |
with ResultsData. | |
@param ResultsData A string returned from an IFR browser or | |
equivalent. The results string will have no | |
routing information in them. | |
@param RetrieveData A BOOLEAN field which allows an agent to retrieve | |
(if RetrieveData = TRUE) data from the uncommitted | |
browser state information or set (if RetrieveData | |
= FALSE) data in the uncommitted browser state | |
information. | |
@param VariableGuid An optional field to indicate the target variable | |
GUID name to use. | |
@param VariableName An optional field to indicate the target | |
human-readable variable name. | |
@retval EFI_SUCCESS The results have been distributed or are awaiting | |
distribution. | |
@retval EFI_BUFFER_TOO_SMALL The ResultsDataSize specified was too small to | |
contain the results data. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BrowserCallback ( | |
IN CONST EFI_FORM_BROWSER2_PROTOCOL *This, | |
IN OUT UINTN *ResultsDataSize, | |
IN OUT EFI_STRING ResultsData, | |
IN BOOLEAN RetrieveData, | |
IN CONST EFI_GUID *VariableGuid, OPTIONAL | |
IN CONST CHAR16 *VariableName OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
BROWSER_STORAGE *Storage; | |
FORMSET_STORAGE *FormsetStorage; | |
UINTN TotalSize; | |
BOOLEAN Found; | |
if (ResultsDataSize == NULL || ResultsData == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
TotalSize = *ResultsDataSize; | |
Storage = NULL; | |
Found = FALSE; | |
Status = EFI_SUCCESS; | |
if (VariableGuid != NULL) { | |
// | |
// Try to find target storage in the current formset. | |
// | |
Link = GetFirstNode (&gBrowserStorageList); | |
while (!IsNull (&gBrowserStorageList, Link)) { | |
Storage = BROWSER_STORAGE_FROM_LINK (Link); | |
Link = GetNextNode (&gBrowserStorageList, Link); | |
// | |
// Check the current storage. | |
// | |
if (!CompareGuid (&Storage->Guid, (EFI_GUID *) VariableGuid)) { | |
continue; | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_BUFFER || | |
Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { | |
// | |
// Buffer storage require both GUID and Name | |
// | |
if (VariableName == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
if (StrCmp (Storage->Name, (CHAR16 *) VariableName) != 0) { | |
continue; | |
} | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE || | |
Storage->Type == EFI_HII_VARSTORE_BUFFER) { | |
if (mSystemLevelFormSet == NULL || mSystemLevelFormSet->HiiHandle == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
if (Storage->HiiHandle != mSystemLevelFormSet->HiiHandle) { | |
continue; | |
} | |
} | |
Status = ProcessStorage (&TotalSize, &ResultsData, RetrieveData, Storage); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { | |
ConfigRequestAdjust (Storage, ResultsData, TRUE); | |
} | |
// | |
// Different formsets may have same varstore, so here just set the flag | |
// not exit the circle. | |
// | |
Found = TRUE; | |
break; | |
} | |
if (!Found) { | |
return EFI_NOT_FOUND; | |
} | |
} else { | |
// | |
// GUID/Name is not specified, take the first storage in FormSet | |
// | |
if (mSystemLevelFormSet == NULL) { | |
return EFI_NOT_READY; | |
} | |
// | |
// Generate <ConfigResp> | |
// | |
Link = GetFirstNode (&mSystemLevelFormSet->StorageListHead); | |
if (IsNull (&mSystemLevelFormSet->StorageListHead, Link)) { | |
return EFI_UNSUPPORTED; | |
} | |
FormsetStorage = FORMSET_STORAGE_FROM_LINK (Link); | |
Status = ProcessStorage (&TotalSize, &ResultsData, RetrieveData, FormsetStorage->BrowserStorage); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
if (RetrieveData) { | |
Status = TotalSize <= *ResultsDataSize ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL; | |
*ResultsDataSize = TotalSize; | |
} | |
return Status; | |
} | |
/** | |
Callback function for SimpleTextInEx protocol install events | |
@param Event the event that is signaled. | |
@param Context not used here. | |
**/ | |
VOID | |
EFIAPI | |
FormDisplayCallback ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
if (mFormDisplay != NULL) { | |
return; | |
} | |
gBS->LocateProtocol ( | |
&gEdkiiFormDisplayEngineProtocolGuid, | |
NULL, | |
(VOID **) &mFormDisplay | |
); | |
} | |
/** | |
Initialize Setup Browser driver. | |
@param ImageHandle The image handle. | |
@param SystemTable The system table. | |
@retval EFI_SUCCESS The Setup Browser module is initialized correctly.. | |
@return Other value if failed to initialize the Setup Browser module. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InitializeSetup ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
VOID *Registration; | |
// | |
// Locate required Hii relative protocols | |
// | |
Status = gBS->LocateProtocol ( | |
&gEfiHiiDatabaseProtocolGuid, | |
NULL, | |
(VOID **) &mHiiDatabase | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = gBS->LocateProtocol ( | |
&gEfiHiiConfigRoutingProtocolGuid, | |
NULL, | |
(VOID **) &mHiiConfigRouting | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = gBS->LocateProtocol ( | |
&gEfiDevicePathFromTextProtocolGuid, | |
NULL, | |
(VOID **) &mPathFromText | |
); | |
// | |
// Install FormBrowser2 protocol | |
// | |
mPrivateData.Handle = NULL; | |
Status = gBS->InstallProtocolInterface ( | |
&mPrivateData.Handle, | |
&gEfiFormBrowser2ProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mPrivateData.FormBrowser2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Install FormBrowserEx2 protocol | |
// | |
InitializeListHead (&mPrivateData.FormBrowserEx2.FormViewHistoryHead); | |
InitializeListHead (&mPrivateData.FormBrowserEx2.OverrideQestListHead); | |
mPrivateData.Handle = NULL; | |
Status = gBS->InstallProtocolInterface ( | |
&mPrivateData.Handle, | |
&gEdkiiFormBrowserEx2ProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mPrivateData.FormBrowserEx2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = gBS->InstallProtocolInterface ( | |
&mPrivateData.Handle, | |
&gEfiFormBrowserExProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mPrivateData.FormBrowserEx | |
); | |
ASSERT_EFI_ERROR (Status); | |
InitializeDisplayFormData (); | |
Status = gBS->LocateProtocol ( | |
&gEdkiiFormDisplayEngineProtocolGuid, | |
NULL, | |
(VOID **) &mFormDisplay | |
); | |
if (EFI_ERROR (Status)) { | |
EfiCreateProtocolNotifyEvent ( | |
&gEdkiiFormDisplayEngineProtocolGuid, | |
TPL_CALLBACK, | |
FormDisplayCallback, | |
NULL, | |
&Registration | |
); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Create a new string in HII Package List. | |
@param String The String to be added | |
@param HiiHandle The package list in the HII database to insert the | |
specified string. | |
@return The output string. | |
**/ | |
EFI_STRING_ID | |
NewString ( | |
IN CHAR16 *String, | |
IN EFI_HII_HANDLE HiiHandle | |
) | |
{ | |
EFI_STRING_ID StringId; | |
StringId = HiiSetString (HiiHandle, 0, String, NULL); | |
ASSERT (StringId != 0); | |
return StringId; | |
} | |
/** | |
Delete a string from HII Package List. | |
@param StringId Id of the string in HII database. | |
@param HiiHandle The HII package list handle. | |
@retval EFI_SUCCESS The string was deleted successfully. | |
**/ | |
EFI_STATUS | |
DeleteString ( | |
IN EFI_STRING_ID StringId, | |
IN EFI_HII_HANDLE HiiHandle | |
) | |
{ | |
CHAR16 NullChar; | |
NullChar = CHAR_NULL; | |
HiiSetString (HiiHandle, StringId, &NullChar, NULL); | |
return EFI_SUCCESS; | |
} | |
/** | |
Get the string based on the StringId and HII Package List Handle. | |
@param Token The String's ID. | |
@param HiiHandle The package list in the HII database to search for | |
the specified string. | |
@return The output string. | |
**/ | |
CHAR16 * | |
GetToken ( | |
IN EFI_STRING_ID Token, | |
IN EFI_HII_HANDLE HiiHandle | |
) | |
{ | |
EFI_STRING String; | |
if (HiiHandle == NULL) { | |
return NULL; | |
} | |
String = HiiGetString (HiiHandle, Token, NULL); | |
if (String == NULL) { | |
String = AllocateCopyPool (StrSize (mUnknownString), mUnknownString); | |
ASSERT (String != NULL); | |
} | |
return (CHAR16 *) String; | |
} | |
/** | |
Allocate new memory and then copy the Unicode string Source to Destination. | |
@param Dest Location to copy string | |
@param Src String to copy | |
**/ | |
VOID | |
NewStringCpy ( | |
IN OUT CHAR16 **Dest, | |
IN CHAR16 *Src | |
) | |
{ | |
if (*Dest != NULL) { | |
FreePool (*Dest); | |
} | |
*Dest = AllocateCopyPool (StrSize (Src), Src); | |
ASSERT (*Dest != NULL); | |
} | |
/** | |
Allocate new memory and concatinate Source on the end of Destination. | |
@param Dest String to added to the end of. | |
@param Src String to concatinate. | |
**/ | |
VOID | |
NewStringCat ( | |
IN OUT CHAR16 **Dest, | |
IN CHAR16 *Src | |
) | |
{ | |
CHAR16 *NewString; | |
UINTN TmpSize; | |
if (*Dest == NULL) { | |
NewStringCpy (Dest, Src); | |
return; | |
} | |
TmpSize = StrSize (*Dest); | |
NewString = AllocateZeroPool (TmpSize + StrSize (Src) - 1); | |
ASSERT (NewString != NULL); | |
StrCpy (NewString, *Dest); | |
StrCat (NewString, Src); | |
FreePool (*Dest); | |
*Dest = NewString; | |
} | |
/** | |
Get Value for given Name from a NameValue Storage. | |
@param Storage The NameValue Storage. | |
@param Name The Name. | |
@param Value The retured Value. | |
@param GetValueFrom Where to get source value, from EditValue or Value. | |
@retval EFI_SUCCESS Value found for given Name. | |
@retval EFI_NOT_FOUND No such Name found in NameValue storage. | |
**/ | |
EFI_STATUS | |
GetValueByName ( | |
IN BROWSER_STORAGE *Storage, | |
IN CHAR16 *Name, | |
IN OUT CHAR16 **Value, | |
IN GET_SET_QUESTION_VALUE_WITH GetValueFrom | |
) | |
{ | |
LIST_ENTRY *Link; | |
NAME_VALUE_NODE *Node; | |
if (GetValueFrom != GetSetValueWithEditBuffer && GetValueFrom != GetSetValueWithBuffer) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*Value = NULL; | |
Link = GetFirstNode (&Storage->NameValueListHead); | |
while (!IsNull (&Storage->NameValueListHead, Link)) { | |
Node = NAME_VALUE_NODE_FROM_LINK (Link); | |
if (StrCmp (Name, Node->Name) == 0) { | |
if (GetValueFrom == GetSetValueWithEditBuffer) { | |
NewStringCpy (Value, Node->EditValue); | |
} else { | |
NewStringCpy (Value, Node->Value); | |
} | |
return EFI_SUCCESS; | |
} | |
Link = GetNextNode (&Storage->NameValueListHead, Link); | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Set Value of given Name in a NameValue Storage. | |
@param Storage The NameValue Storage. | |
@param Name The Name. | |
@param Value The Value to set. | |
@param SetValueTo Whether update editValue or Value. | |
@param ReturnNode The node use the input name. | |
@retval EFI_SUCCESS Value found for given Name. | |
@retval EFI_NOT_FOUND No such Name found in NameValue storage. | |
**/ | |
EFI_STATUS | |
SetValueByName ( | |
IN BROWSER_STORAGE *Storage, | |
IN CHAR16 *Name, | |
IN CHAR16 *Value, | |
IN GET_SET_QUESTION_VALUE_WITH SetValueTo, | |
OUT NAME_VALUE_NODE **ReturnNode | |
) | |
{ | |
LIST_ENTRY *Link; | |
NAME_VALUE_NODE *Node; | |
CHAR16 *Buffer; | |
if (SetValueTo != GetSetValueWithEditBuffer && SetValueTo != GetSetValueWithBuffer) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Link = GetFirstNode (&Storage->NameValueListHead); | |
while (!IsNull (&Storage->NameValueListHead, Link)) { | |
Node = NAME_VALUE_NODE_FROM_LINK (Link); | |
if (StrCmp (Name, Node->Name) == 0) { | |
if (SetValueTo == GetSetValueWithEditBuffer) { | |
Buffer = Node->EditValue; | |
} else { | |
Buffer = Node->Value; | |
} | |
if (Buffer != NULL) { | |
FreePool (Buffer); | |
} | |
Buffer = AllocateCopyPool (StrSize (Value), Value); | |
ASSERT (Buffer != NULL); | |
if (SetValueTo == GetSetValueWithEditBuffer) { | |
Node->EditValue = Buffer; | |
} else { | |
Node->Value = Buffer; | |
} | |
if (ReturnNode != NULL) { | |
*ReturnNode = Node; | |
} | |
return EFI_SUCCESS; | |
} | |
Link = GetNextNode (&Storage->NameValueListHead, Link); | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Convert setting of Buffer Storage or NameValue Storage to <ConfigResp>. | |
@param Storage The Storage to be conveted. | |
@param ConfigResp The returned <ConfigResp>. | |
@param ConfigRequest The ConfigRequest string. | |
@param GetEditBuf Get the data from editbuffer or buffer. | |
@retval EFI_SUCCESS Convert success. | |
@retval EFI_INVALID_PARAMETER Incorrect storage type. | |
**/ | |
EFI_STATUS | |
StorageToConfigResp ( | |
IN BROWSER_STORAGE *Storage, | |
IN CHAR16 **ConfigResp, | |
IN CHAR16 *ConfigRequest, | |
IN BOOLEAN GetEditBuf | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STRING Progress; | |
LIST_ENTRY *Link; | |
NAME_VALUE_NODE *Node; | |
UINT8 *SourceBuf; | |
FORMSET_STORAGE *FormsetStorage; | |
Status = EFI_SUCCESS; | |
switch (Storage->Type) { | |
case EFI_HII_VARSTORE_BUFFER: | |
case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: | |
SourceBuf = GetEditBuf ? Storage->EditBuffer : Storage->Buffer; | |
Status = mHiiConfigRouting->BlockToConfig ( | |
mHiiConfigRouting, | |
ConfigRequest, | |
SourceBuf, | |
Storage->Size, | |
ConfigResp, | |
&Progress | |
); | |
break; | |
case EFI_HII_VARSTORE_NAME_VALUE: | |
*ConfigResp = NULL; | |
FormsetStorage = GetFstStgFromBrsStg(Storage); | |
ASSERT (FormsetStorage != NULL); | |
NewStringCat (ConfigResp, FormsetStorage->ConfigHdr); | |
Link = GetFirstNode (&Storage->NameValueListHead); | |
while (!IsNull (&Storage->NameValueListHead, Link)) { | |
Node = NAME_VALUE_NODE_FROM_LINK (Link); | |
if (StrStr (ConfigRequest, Node->Name) != NULL) { | |
NewStringCat (ConfigResp, L"&"); | |
NewStringCat (ConfigResp, Node->Name); | |
NewStringCat (ConfigResp, L"="); | |
if (GetEditBuf) { | |
NewStringCat (ConfigResp, Node->EditValue); | |
} else { | |
NewStringCat (ConfigResp, Node->Value); | |
} | |
} | |
Link = GetNextNode (&Storage->NameValueListHead, Link); | |
} | |
break; | |
case EFI_HII_VARSTORE_EFI_VARIABLE: | |
default: | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
return Status; | |
} | |
/** | |
Convert <ConfigResp> to settings in Buffer Storage or NameValue Storage. | |
@param Storage The Storage to receive the settings. | |
@param ConfigResp The <ConfigResp> to be converted. | |
@retval EFI_SUCCESS Convert success. | |
@retval EFI_INVALID_PARAMETER Incorrect storage type. | |
**/ | |
EFI_STATUS | |
ConfigRespToStorage ( | |
IN BROWSER_STORAGE *Storage, | |
IN CHAR16 *ConfigResp | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STRING Progress; | |
UINTN BufferSize; | |
CHAR16 *StrPtr; | |
CHAR16 *Name; | |
CHAR16 *Value; | |
Status = EFI_SUCCESS; | |
switch (Storage->Type) { | |
case EFI_HII_VARSTORE_BUFFER: | |
case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: | |
BufferSize = Storage->Size; | |
Status = mHiiConfigRouting->ConfigToBlock ( | |
mHiiConfigRouting, | |
ConfigResp, | |
Storage->EditBuffer, | |
&BufferSize, | |
&Progress | |
); | |
break; | |
case EFI_HII_VARSTORE_NAME_VALUE: | |
StrPtr = StrStr (ConfigResp, L"PATH"); | |
if (StrPtr == NULL) { | |
break; | |
} | |
StrPtr = StrStr (ConfigResp, L"&"); | |
while (StrPtr != NULL) { | |
// | |
// Skip '&' | |
// | |
StrPtr = StrPtr + 1; | |
Name = StrPtr; | |
StrPtr = StrStr (StrPtr, L"="); | |
if (StrPtr == NULL) { | |
break; | |
} | |
*StrPtr = 0; | |
// | |
// Skip '=' | |
// | |
StrPtr = StrPtr + 1; | |
Value = StrPtr; | |
StrPtr = StrStr (StrPtr, L"&"); | |
if (StrPtr != NULL) { | |
*StrPtr = 0; | |
} | |
SetValueByName (Storage, Name, Value, GetSetValueWithEditBuffer, NULL); | |
} | |
break; | |
case EFI_HII_VARSTORE_EFI_VARIABLE: | |
default: | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
return Status; | |
} | |
/** | |
Convert the buffer value to HiiValue. | |
@param Question The question. | |
@param Value Unicode buffer save the question value. | |
@retval Status whether convert the value success. | |
**/ | |
EFI_STATUS | |
BufferToValue ( | |
IN OUT FORM_BROWSER_STATEMENT *Question, | |
IN CHAR16 *Value | |
) | |
{ | |
CHAR16 *StringPtr; | |
BOOLEAN IsBufferStorage; | |
CHAR16 *DstBuf; | |
CHAR16 TempChar; | |
UINTN LengthStr; | |
UINT8 *Dst; | |
CHAR16 TemStr[5]; | |
UINTN Index; | |
UINT8 DigitUint8; | |
BOOLEAN IsString; | |
UINTN Length; | |
EFI_STATUS Status; | |
IsString = (BOOLEAN) ((Question->HiiValue.Type == EFI_IFR_TYPE_STRING) ? TRUE : FALSE); | |
if (Question->Storage->Type == EFI_HII_VARSTORE_BUFFER || | |
Question->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { | |
IsBufferStorage = TRUE; | |
} else { | |
IsBufferStorage = FALSE; | |
} | |
// | |
// Question Value is provided by Buffer Storage or NameValue Storage | |
// | |
if (Question->BufferValue != NULL) { | |
// | |
// This Question is password or orderedlist | |
// | |
Dst = Question->BufferValue; | |
} else { | |
// | |
// Other type of Questions | |
// | |
Dst = (UINT8 *) &Question->HiiValue.Value; | |
} | |
// | |
// Temp cut at the end of this section, end with '\0' or '&'. | |
// | |
StringPtr = Value; | |
while (*StringPtr != L'\0' && *StringPtr != L'&') { | |
StringPtr++; | |
} | |
TempChar = *StringPtr; | |
*StringPtr = L'\0'; | |
LengthStr = StrLen (Value); | |
Status = EFI_SUCCESS; | |
if (!IsBufferStorage && IsString) { | |
// | |
// Convert Config String to Unicode String, e.g "0041004200430044" => "ABCD" | |
// Add string tail char L'\0' into Length | |
// | |
Length = Question->StorageWidth + sizeof (CHAR16); | |
if (Length < ((LengthStr / 4 + 1) * 2)) { | |
Status = EFI_BUFFER_TOO_SMALL; | |
} else { | |
DstBuf = (CHAR16 *) Dst; | |
ZeroMem (TemStr, sizeof (TemStr)); | |
for (Index = 0; Index < LengthStr; Index += 4) { | |
StrnCpy (TemStr, Value + Index, 4); | |
DstBuf[Index/4] = (CHAR16) StrHexToUint64 (TemStr); | |
} | |
// | |
// Add tailing L'\0' character | |
// | |
DstBuf[Index/4] = L'\0'; | |
} | |
} else { | |
if (Question->StorageWidth < ((LengthStr + 1) / 2)) { | |
Status = EFI_BUFFER_TOO_SMALL; | |
} else { | |
ZeroMem (TemStr, sizeof (TemStr)); | |
for (Index = 0; Index < LengthStr; Index ++) { | |
TemStr[0] = Value[LengthStr - Index - 1]; | |
DigitUint8 = (UINT8) StrHexToUint64 (TemStr); | |
if ((Index & 1) == 0) { | |
Dst [Index/2] = DigitUint8; | |
} else { | |
Dst [Index/2] = (UINT8) ((DigitUint8 << 4) + Dst [Index/2]); | |
} | |
} | |
} | |
} | |
*StringPtr = TempChar; | |
return Status; | |
} | |
/** | |
Get Question's current Value. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param Question Question to be initialized. | |
@param GetValueFrom Where to get value, may from editbuffer, buffer or hii driver. | |
@retval EFI_SUCCESS The function completed successfully. | |
**/ | |
EFI_STATUS | |
GetQuestionValue ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN OUT FORM_BROWSER_STATEMENT *Question, | |
IN GET_SET_QUESTION_VALUE_WITH GetValueFrom | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN Enabled; | |
BOOLEAN Pending; | |
UINT8 *Dst; | |
UINTN StorageWidth; | |
EFI_TIME EfiTime; | |
BROWSER_STORAGE *Storage; | |
FORMSET_STORAGE *FormsetStorage; | |
EFI_IFR_TYPE_VALUE *QuestionValue; | |
CHAR16 *ConfigRequest; | |
CHAR16 *Progress; | |
CHAR16 *Result; | |
CHAR16 *Value; | |
UINTN Length; | |
BOOLEAN IsBufferStorage; | |
Status = EFI_SUCCESS; | |
Value = NULL; | |
Result = NULL; | |
if (GetValueFrom >= GetSetValueWithMax) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Question value is provided by an Expression, evaluate it | |
// | |
if (Question->ValueExpression != NULL) { | |
Status = EvaluateExpression (FormSet, Form, Question->ValueExpression); | |
if (!EFI_ERROR (Status)) { | |
if (Question->ValueExpression->Result.Type == EFI_IFR_TYPE_BUFFER) { | |
ASSERT (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER && Question->HiiValue.Buffer != NULL); | |
if (Question->StorageWidth > Question->ValueExpression->Result.BufferLen) { | |
CopyMem (Question->HiiValue.Buffer, Question->ValueExpression->Result.Buffer, Question->ValueExpression->Result.BufferLen); | |
Question->HiiValue.BufferLen = Question->ValueExpression->Result.BufferLen; | |
} else { | |
CopyMem (Question->HiiValue.Buffer, Question->ValueExpression->Result.Buffer, Question->StorageWidth); | |
Question->HiiValue.BufferLen = Question->StorageWidth; | |
} | |
FreePool (Question->ValueExpression->Result.Buffer); | |
} | |
Question->HiiValue.Type = Question->ValueExpression->Result.Type; | |
CopyMem (&Question->HiiValue.Value, &Question->ValueExpression->Result.Value, sizeof (EFI_IFR_TYPE_VALUE)); | |
} | |
return Status; | |
} | |
// | |
// Get question value by read expression. | |
// | |
if (Question->ReadExpression != NULL && Form->FormType == STANDARD_MAP_FORM_TYPE) { | |
Status = EvaluateExpression (FormSet, Form, Question->ReadExpression); | |
if (!EFI_ERROR (Status) && | |
((Question->ReadExpression->Result.Type < EFI_IFR_TYPE_OTHER) || (Question->ReadExpression->Result.Type == EFI_IFR_TYPE_BUFFER))) { | |
// | |
// Only update question value to the valid result. | |
// | |
if (Question->ReadExpression->Result.Type == EFI_IFR_TYPE_BUFFER) { | |
ASSERT (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER && Question->HiiValue.Buffer != NULL); | |
if (Question->StorageWidth > Question->ReadExpression->Result.BufferLen) { | |
CopyMem (Question->HiiValue.Buffer, Question->ReadExpression->Result.Buffer, Question->ReadExpression->Result.BufferLen); | |
Question->HiiValue.BufferLen = Question->ReadExpression->Result.BufferLen; | |
} else { | |
CopyMem (Question->HiiValue.Buffer, Question->ReadExpression->Result.Buffer, Question->StorageWidth); | |
Question->HiiValue.BufferLen = Question->StorageWidth; | |
} | |
FreePool (Question->ReadExpression->Result.Buffer); | |
} | |
Question->HiiValue.Type = Question->ReadExpression->Result.Type; | |
CopyMem (&Question->HiiValue.Value, &Question->ReadExpression->Result.Value, sizeof (EFI_IFR_TYPE_VALUE)); | |
return EFI_SUCCESS; | |
} | |
} | |
// | |
// Question value is provided by RTC | |
// | |
Storage = Question->Storage; | |
QuestionValue = &Question->HiiValue.Value; | |
if (Storage == NULL) { | |
// | |
// It's a Question without storage, or RTC date/time | |
// | |
if (Question->Operand == EFI_IFR_DATE_OP || Question->Operand == EFI_IFR_TIME_OP) { | |
// | |
// Date and time define the same Flags bit | |
// | |
switch (Question->Flags & EFI_QF_DATE_STORAGE) { | |
case QF_DATE_STORAGE_TIME: | |
Status = gRT->GetTime (&EfiTime, NULL); | |
break; | |
case QF_DATE_STORAGE_WAKEUP: | |
Status = gRT->GetWakeupTime (&Enabled, &Pending, &EfiTime); | |
break; | |
case QF_DATE_STORAGE_NORMAL: | |
default: | |
// | |
// For date/time without storage | |
// | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Question->Operand == EFI_IFR_DATE_OP) { | |
QuestionValue->date.Year = EfiTime.Year; | |
QuestionValue->date.Month = EfiTime.Month; | |
QuestionValue->date.Day = EfiTime.Day; | |
} else { | |
QuestionValue->time.Hour = EfiTime.Hour; | |
QuestionValue->time.Minute = EfiTime.Minute; | |
QuestionValue->time.Second = EfiTime.Second; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
// | |
// Question value is provided by EFI variable | |
// | |
StorageWidth = Question->StorageWidth; | |
if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
if (Question->BufferValue != NULL) { | |
Dst = Question->BufferValue; | |
} else { | |
Dst = (UINT8 *) QuestionValue; | |
} | |
Status = gRT->GetVariable ( | |
Question->VariableName, | |
&Storage->Guid, | |
NULL, | |
&StorageWidth, | |
Dst | |
); | |
// | |
// Always return success, even this EFI variable doesn't exist | |
// | |
return EFI_SUCCESS; | |
} | |
// | |
// Question Value is provided by Buffer Storage or NameValue Storage | |
// | |
if (Question->BufferValue != NULL) { | |
// | |
// This Question is password or orderedlist | |
// | |
Dst = Question->BufferValue; | |
} else { | |
// | |
// Other type of Questions | |
// | |
Dst = (UINT8 *) &Question->HiiValue.Value; | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_BUFFER || | |
Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { | |
IsBufferStorage = TRUE; | |
} else { | |
IsBufferStorage = FALSE; | |
} | |
if (GetValueFrom == GetSetValueWithEditBuffer || GetValueFrom == GetSetValueWithBuffer ) { | |
if (IsBufferStorage) { | |
if (GetValueFrom == GetSetValueWithEditBuffer) { | |
// | |
// Copy from storage Edit buffer | |
// | |
CopyMem (Dst, Storage->EditBuffer + Question->VarStoreInfo.VarOffset, StorageWidth); | |
} else { | |
// | |
// Copy from storage Edit buffer | |
// | |
CopyMem (Dst, Storage->Buffer + Question->VarStoreInfo.VarOffset, StorageWidth); | |
} | |
} else { | |
Value = NULL; | |
Status = GetValueByName (Storage, Question->VariableName, &Value, GetValueFrom); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ASSERT (Value != NULL); | |
Status = BufferToValue (Question, Value); | |
FreePool (Value); | |
} | |
} else { | |
FormsetStorage = GetFstStgFromVarId(FormSet, Question->VarStoreId); | |
ASSERT (FormsetStorage != NULL); | |
// | |
// <ConfigRequest> ::= <ConfigHdr> + <BlockName> || | |
// <ConfigHdr> + "&" + <VariableName> | |
// | |
if (IsBufferStorage) { | |
Length = StrLen (FormsetStorage->ConfigHdr); | |
Length += StrLen (Question->BlockName); | |
} else { | |
Length = StrLen (FormsetStorage->ConfigHdr); | |
Length += StrLen (Question->VariableName) + 1; | |
} | |
ConfigRequest = AllocateZeroPool ((Length + 1) * sizeof (CHAR16)); | |
ASSERT (ConfigRequest != NULL); | |
StrCpy (ConfigRequest, FormsetStorage->ConfigHdr); | |
if (IsBufferStorage) { | |
StrCat (ConfigRequest, Question->BlockName); | |
} else { | |
StrCat (ConfigRequest, L"&"); | |
StrCat (ConfigRequest, Question->VariableName); | |
} | |
// | |
// Request current settings from Configuration Driver | |
// | |
Status = mHiiConfigRouting->ExtractConfig ( | |
mHiiConfigRouting, | |
ConfigRequest, | |
&Progress, | |
&Result | |
); | |
FreePool (ConfigRequest); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Skip <ConfigRequest> | |
// | |
if (IsBufferStorage) { | |
Value = StrStr (Result, L"&VALUE"); | |
if (Value == NULL) { | |
FreePool (Result); | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Skip "&VALUE" | |
// | |
Value = Value + 6; | |
} else { | |
Value = Result + Length; | |
} | |
if (*Value != '=') { | |
FreePool (Result); | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Skip '=', point to value | |
// | |
Value = Value + 1; | |
Status = BufferToValue (Question, Value); | |
if (EFI_ERROR (Status)) { | |
FreePool (Result); | |
return Status; | |
} | |
// | |
// Synchronize Edit Buffer | |
// | |
if (IsBufferStorage) { | |
CopyMem (Storage->EditBuffer + Question->VarStoreInfo.VarOffset, Dst, StorageWidth); | |
} else { | |
SetValueByName (Storage, Question->VariableName, Value, GetSetValueWithEditBuffer, NULL); | |
} | |
if (Result != NULL) { | |
FreePool (Result); | |
} | |
} | |
return Status; | |
} | |
/** | |
Save Question Value to edit copy(cached) or Storage(uncached). | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param Question Pointer to the Question. | |
@param SetValueTo Update the question value to editbuffer , buffer or hii driver. | |
@retval EFI_SUCCESS The function completed successfully. | |
**/ | |
EFI_STATUS | |
SetQuestionValue ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN OUT FORM_BROWSER_STATEMENT *Question, | |
IN GET_SET_QUESTION_VALUE_WITH SetValueTo | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN Enabled; | |
BOOLEAN Pending; | |
UINT8 *Src; | |
EFI_TIME EfiTime; | |
UINTN BufferLen; | |
UINTN StorageWidth; | |
BROWSER_STORAGE *Storage; | |
FORMSET_STORAGE *FormsetStorage; | |
EFI_IFR_TYPE_VALUE *QuestionValue; | |
CHAR16 *ConfigResp; | |
CHAR16 *Progress; | |
CHAR16 *Value; | |
UINTN Length; | |
BOOLEAN IsBufferStorage; | |
BOOLEAN IsString; | |
UINT8 *TemBuffer; | |
CHAR16 *TemName; | |
CHAR16 *TemString; | |
UINTN Index; | |
NAME_VALUE_NODE *Node; | |
Status = EFI_SUCCESS; | |
Node = NULL; | |
if (SetValueTo >= GetSetValueWithMax) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// If Question value is provided by an Expression, then it is read only | |
// | |
if (Question->ValueExpression != NULL) { | |
return Status; | |
} | |
// | |
// Before set question value, evaluate its write expression. | |
// | |
if (Question->WriteExpression != NULL && Form->FormType == STANDARD_MAP_FORM_TYPE) { | |
Status = EvaluateExpression (FormSet, Form, Question->WriteExpression); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
// | |
// Question value is provided by RTC | |
// | |
Storage = Question->Storage; | |
QuestionValue = &Question->HiiValue.Value; | |
if (Storage == NULL) { | |
// | |
// It's a Question without storage, or RTC date/time | |
// | |
if (Question->Operand == EFI_IFR_DATE_OP || Question->Operand == EFI_IFR_TIME_OP) { | |
// | |
// Date and time define the same Flags bit | |
// | |
switch (Question->Flags & EFI_QF_DATE_STORAGE) { | |
case QF_DATE_STORAGE_TIME: | |
Status = gRT->GetTime (&EfiTime, NULL); | |
break; | |
case QF_DATE_STORAGE_WAKEUP: | |
Status = gRT->GetWakeupTime (&Enabled, &Pending, &EfiTime); | |
break; | |
case QF_DATE_STORAGE_NORMAL: | |
default: | |
// | |
// For date/time without storage | |
// | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Question->Operand == EFI_IFR_DATE_OP) { | |
EfiTime.Year = QuestionValue->date.Year; | |
EfiTime.Month = QuestionValue->date.Month; | |
EfiTime.Day = QuestionValue->date.Day; | |
} else { | |
EfiTime.Hour = QuestionValue->time.Hour; | |
EfiTime.Minute = QuestionValue->time.Minute; | |
EfiTime.Second = QuestionValue->time.Second; | |
} | |
if ((Question->Flags & EFI_QF_DATE_STORAGE) == QF_DATE_STORAGE_TIME) { | |
Status = gRT->SetTime (&EfiTime); | |
} else { | |
Status = gRT->SetWakeupTime (TRUE, &EfiTime); | |
} | |
} | |
return Status; | |
} | |
// | |
// Question value is provided by EFI variable | |
// | |
StorageWidth = Question->StorageWidth; | |
if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
if (Question->BufferValue != NULL) { | |
Src = Question->BufferValue; | |
} else { | |
Src = (UINT8 *) QuestionValue; | |
} | |
Status = gRT->SetVariable ( | |
Question->VariableName, | |
&Storage->Guid, | |
Storage->Attributes, | |
StorageWidth, | |
Src | |
); | |
return Status; | |
} | |
// | |
// Question Value is provided by Buffer Storage or NameValue Storage | |
// | |
if (Question->BufferValue != NULL) { | |
Src = Question->BufferValue; | |
} else { | |
Src = (UINT8 *) &Question->HiiValue.Value; | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_BUFFER || | |
Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { | |
IsBufferStorage = TRUE; | |
} else { | |
IsBufferStorage = FALSE; | |
} | |
IsString = (BOOLEAN) ((Question->HiiValue.Type == EFI_IFR_TYPE_STRING) ? TRUE : FALSE); | |
if (SetValueTo == GetSetValueWithEditBuffer || SetValueTo == GetSetValueWithBuffer) { | |
if (IsBufferStorage) { | |
if (SetValueTo == GetSetValueWithEditBuffer) { | |
// | |
// Copy to storage edit buffer | |
// | |
CopyMem (Storage->EditBuffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); | |
} else if (SetValueTo == GetSetValueWithBuffer) { | |
// | |
// Copy to storage edit buffer | |
// | |
CopyMem (Storage->Buffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); | |
} | |
} else { | |
if (IsString) { | |
// | |
// Allocate enough string buffer. | |
// | |
Value = NULL; | |
BufferLen = ((StrLen ((CHAR16 *) Src) * 4) + 1) * sizeof (CHAR16); | |
Value = AllocateZeroPool (BufferLen); | |
ASSERT (Value != NULL); | |
// | |
// Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044" | |
// | |
TemName = (CHAR16 *) Src; | |
TemString = Value; | |
for (; *TemName != L'\0'; TemName++) { | |
TemString += UnicodeValueToString (TemString, PREFIX_ZERO | RADIX_HEX, *TemName, 4); | |
} | |
} else { | |
BufferLen = StorageWidth * 2 + 1; | |
Value = AllocateZeroPool (BufferLen * sizeof (CHAR16)); | |
ASSERT (Value != NULL); | |
// | |
// Convert Buffer to Hex String | |
// | |
TemBuffer = Src + StorageWidth - 1; | |
TemString = Value; | |
for (Index = 0; Index < StorageWidth; Index ++, TemBuffer --) { | |
TemString += UnicodeValueToString (TemString, PREFIX_ZERO | RADIX_HEX, *TemBuffer, 2); | |
} | |
} | |
Status = SetValueByName (Storage, Question->VariableName, Value, SetValueTo, &Node); | |
FreePool (Value); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
} else if (SetValueTo == GetSetValueWithHiiDriver) { | |
// | |
// <ConfigResp> ::= <ConfigHdr> + <BlockName> + "&VALUE=" + "<HexCh>StorageWidth * 2" || | |
// <ConfigHdr> + "&" + <VariableName> + "=" + "<string>" | |
// | |
if (IsBufferStorage) { | |
Length = StrLen (Question->BlockName) + 7; | |
} else { | |
Length = StrLen (Question->VariableName) + 2; | |
} | |
if (!IsBufferStorage && IsString) { | |
Length += (StrLen ((CHAR16 *) Src) * 4); | |
} else { | |
Length += (StorageWidth * 2); | |
} | |
FormsetStorage = GetFstStgFromVarId(FormSet, Question->VarStoreId); | |
ASSERT (FormsetStorage != NULL); | |
ConfigResp = AllocateZeroPool ((StrLen (FormsetStorage->ConfigHdr) + Length + 1) * sizeof (CHAR16)); | |
ASSERT (ConfigResp != NULL); | |
StrCpy (ConfigResp, FormsetStorage->ConfigHdr); | |
if (IsBufferStorage) { | |
StrCat (ConfigResp, Question->BlockName); | |
StrCat (ConfigResp, L"&VALUE="); | |
} else { | |
StrCat (ConfigResp, L"&"); | |
StrCat (ConfigResp, Question->VariableName); | |
StrCat (ConfigResp, L"="); | |
} | |
Value = ConfigResp + StrLen (ConfigResp); | |
if (!IsBufferStorage && IsString) { | |
// | |
// Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044" | |
// | |
TemName = (CHAR16 *) Src; | |
TemString = Value; | |
for (; *TemName != L'\0'; TemName++) { | |
TemString += UnicodeValueToString (TemString, PREFIX_ZERO | RADIX_HEX, *TemName, 4); | |
} | |
} else { | |
// | |
// Convert Buffer to Hex String | |
// | |
TemBuffer = Src + StorageWidth - 1; | |
TemString = Value; | |
for (Index = 0; Index < StorageWidth; Index ++, TemBuffer --) { | |
TemString += UnicodeValueToString (TemString, PREFIX_ZERO | RADIX_HEX, *TemBuffer, 2); | |
} | |
} | |
// | |
// Convert to lower char. | |
// | |
for (TemString = Value; *Value != L'\0'; Value++) { | |
if (*Value >= L'A' && *Value <= L'Z') { | |
*Value = (CHAR16) (*Value - L'A' + L'a'); | |
} | |
} | |
// | |
// Submit Question Value to Configuration Driver | |
// | |
Status = mHiiConfigRouting->RouteConfig ( | |
mHiiConfigRouting, | |
ConfigResp, | |
&Progress | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (ConfigResp); | |
return Status; | |
} | |
FreePool (ConfigResp); | |
// | |
// Sync storage, from editbuffer to buffer. | |
// | |
CopyMem (Storage->Buffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); | |
} | |
return Status; | |
} | |
/** | |
Perform nosubmitif check for a Form. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param Question The Question to be validated. | |
@param Type Validation type: NoSubmit | |
@retval EFI_SUCCESS Form validation pass. | |
@retval other Form validation failed. | |
**/ | |
EFI_STATUS | |
ValidateQuestion ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN FORM_BROWSER_STATEMENT *Question, | |
IN UINTN Type | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
LIST_ENTRY *ListHead; | |
FORM_EXPRESSION *Expression; | |
UINT32 BrowserStatus; | |
CHAR16 *ErrorStr; | |
BrowserStatus = BROWSER_SUCCESS; | |
ErrorStr = NULL; | |
switch (Type) { | |
case EFI_HII_EXPRESSION_INCONSISTENT_IF: | |
ListHead = &Question->InconsistentListHead; | |
break; | |
case EFI_HII_EXPRESSION_WARNING_IF: | |
ListHead = &Question->WarningListHead; | |
break; | |
case EFI_HII_EXPRESSION_NO_SUBMIT_IF: | |
ListHead = &Question->NoSubmitListHead; | |
break; | |
default: | |
ASSERT (FALSE); | |
return EFI_UNSUPPORTED; | |
} | |
Link = GetFirstNode (ListHead); | |
while (!IsNull (ListHead, Link)) { | |
Expression = FORM_EXPRESSION_FROM_LINK (Link); | |
// | |
// Evaluate the expression | |
// | |
Status = EvaluateExpression (FormSet, Form, Expression); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (IsTrue (&Expression->Result)) { | |
switch (Type) { | |
case EFI_HII_EXPRESSION_INCONSISTENT_IF: | |
BrowserStatus = BROWSER_INCONSISTENT_IF; | |
break; | |
case EFI_HII_EXPRESSION_WARNING_IF: | |
BrowserStatus = BROWSER_WARNING_IF; | |
break; | |
case EFI_HII_EXPRESSION_NO_SUBMIT_IF: | |
BrowserStatus = BROWSER_NO_SUBMIT_IF; | |
// | |
// This code only used to compatible with old display engine, | |
// New display engine will not use this field. | |
// | |
if (Expression->Error != 0) { | |
ErrorStr = GetToken (Expression->Error, FormSet->HiiHandle); | |
} | |
break; | |
default: | |
ASSERT (FALSE); | |
break; | |
} | |
if (!((Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF) && mSystemSubmit)) { | |
// | |
// If in system submit process and for no_submit_if check, not popup this error message. | |
// Will process this fail again later in not system submit process. | |
// | |
PopupErrorMessage(BrowserStatus, FormSet->HiiHandle, Expression->OpCode, ErrorStr); | |
} | |
if (ErrorStr != NULL) { | |
FreePool (ErrorStr); | |
} | |
if (Type == EFI_HII_EXPRESSION_WARNING_IF) { | |
return EFI_SUCCESS; | |
} else { | |
return EFI_NOT_READY; | |
} | |
} | |
Link = GetNextNode (ListHead, Link); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Perform question check. | |
If one question has more than one check, process form high priority to low. | |
Only one error info will be popup. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param Question The Question to be validated. | |
@retval EFI_SUCCESS Form validation pass. | |
@retval other Form validation failed. | |
**/ | |
EFI_STATUS | |
ValueChangedValidation ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN FORM_BROWSER_STATEMENT *Question | |
) | |
{ | |
EFI_STATUS Status; | |
Status = EFI_SUCCESS; | |
// | |
// Do the inconsistentif check. | |
// | |
if (!IsListEmpty (&Question->InconsistentListHead)) { | |
Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
// | |
// Do the warningif check. | |
// | |
if (!IsListEmpty (&Question->WarningListHead)) { | |
Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_WARNING_IF); | |
} | |
return Status; | |
} | |
/** | |
Perform NoSubmit check for each Form in FormSet. | |
@param FormSet FormSet data structure. | |
@param CurrentForm Current input form data structure. | |
@param Statement The statement for this check. | |
@retval EFI_SUCCESS Form validation pass. | |
@retval other Form validation failed. | |
**/ | |
EFI_STATUS | |
NoSubmitCheck ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN OUT FORM_BROWSER_FORM **CurrentForm, | |
OUT FORM_BROWSER_STATEMENT **Statement | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
FORM_BROWSER_STATEMENT *Question; | |
FORM_BROWSER_FORM *Form; | |
LIST_ENTRY *LinkForm; | |
LinkForm = GetFirstNode (&FormSet->FormListHead); | |
while (!IsNull (&FormSet->FormListHead, LinkForm)) { | |
Form = FORM_BROWSER_FORM_FROM_LINK (LinkForm); | |
LinkForm = GetNextNode (&FormSet->FormListHead, LinkForm); | |
if (*CurrentForm != NULL && *CurrentForm != Form) { | |
continue; | |
} | |
Link = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, Link)) { | |
Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); | |
Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_NO_SUBMIT_IF); | |
if (EFI_ERROR (Status)) { | |
if (*CurrentForm == NULL) { | |
*CurrentForm = Form; | |
} | |
if (Statement != NULL) { | |
*Statement = Question; | |
} | |
return Status; | |
} | |
Link = GetNextNode (&Form->StatementListHead, Link); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Fill storage's edit copy with settings requested from Configuration Driver. | |
@param Storage The storage which need to sync. | |
@param ConfigRequest The config request string which used to sync storage. | |
@param SyncOrRestore Sync the buffer to editbuffer or Restore the | |
editbuffer to buffer | |
if TRUE, copy the editbuffer to the buffer. | |
if FALSE, copy the buffer to the editbuffer. | |
@retval EFI_SUCCESS The function completed successfully. | |
**/ | |
EFI_STATUS | |
SynchronizeStorage ( | |
OUT BROWSER_STORAGE *Storage, | |
IN CHAR16 *ConfigRequest, | |
IN BOOLEAN SyncOrRestore | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STRING Progress; | |
EFI_STRING Result; | |
UINTN BufferSize; | |
LIST_ENTRY *Link; | |
NAME_VALUE_NODE *Node; | |
UINT8 *Src; | |
UINT8 *Dst; | |
Status = EFI_SUCCESS; | |
Result = NULL; | |
if (Storage->Type == EFI_HII_VARSTORE_BUFFER || | |
(Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { | |
BufferSize = Storage->Size; | |
if (SyncOrRestore) { | |
Src = Storage->EditBuffer; | |
Dst = Storage->Buffer; | |
} else { | |
Src = Storage->Buffer; | |
Dst = Storage->EditBuffer; | |
} | |
if (ConfigRequest != NULL) { | |
Status = mHiiConfigRouting->BlockToConfig( | |
mHiiConfigRouting, | |
ConfigRequest, | |
Src, | |
BufferSize, | |
&Result, | |
&Progress | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = mHiiConfigRouting->ConfigToBlock ( | |
mHiiConfigRouting, | |
Result, | |
Dst, | |
&BufferSize, | |
&Progress | |
); | |
if (Result != NULL) { | |
FreePool (Result); | |
} | |
} else { | |
CopyMem (Dst, Src, BufferSize); | |
} | |
} else if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
Link = GetFirstNode (&Storage->NameValueListHead); | |
while (!IsNull (&Storage->NameValueListHead, Link)) { | |
Node = NAME_VALUE_NODE_FROM_LINK (Link); | |
if ((ConfigRequest != NULL && StrStr (ConfigRequest, Node->Name) != NULL) || | |
(ConfigRequest == NULL)) { | |
if (SyncOrRestore) { | |
NewStringCpy (&Node->Value, Node->EditValue); | |
} else { | |
NewStringCpy (&Node->EditValue, Node->Value); | |
} | |
} | |
Link = GetNextNode (&Storage->NameValueListHead, Link); | |
} | |
} | |
return Status; | |
} | |
/** | |
When discard the question value, call the callback function with Changed type | |
to inform the hii driver. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
**/ | |
VOID | |
SendDiscardInfoToDriver ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_BROWSER_STATEMENT *Question; | |
EFI_IFR_TYPE_VALUE *TypeValue; | |
EFI_BROWSER_ACTION_REQUEST ActionRequest; | |
if (FormSet->ConfigAccess == NULL) { | |
return; | |
} | |
Link = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, Link)) { | |
Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); | |
Link = GetNextNode (&Form->StatementListHead, Link); | |
if (Question->Storage == NULL || Question->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
continue; | |
} | |
if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != EFI_IFR_FLAG_CALLBACK) { | |
continue; | |
} | |
if (Question->Operand == EFI_IFR_PASSWORD_OP) { | |
continue; | |
} | |
if (!Question->ValueChanged) { | |
continue; | |
} | |
// | |
// Restore the question value before call the CHANGED callback type. | |
// | |
GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); | |
if (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER) { | |
TypeValue = (EFI_IFR_TYPE_VALUE *) Question->BufferValue; | |
} else { | |
TypeValue = &Question->HiiValue.Value; | |
} | |
ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; | |
FormSet->ConfigAccess->Callback ( | |
FormSet->ConfigAccess, | |
EFI_BROWSER_ACTION_CHANGED, | |
Question->QuestionId, | |
Question->HiiValue.Type, | |
TypeValue, | |
&ActionRequest | |
); | |
} | |
} | |
/** | |
Validate the HiiHandle. | |
@param HiiHandle The input HiiHandle which need to validate. | |
@retval TRUE The handle is validate. | |
@retval FALSE The handle is invalidate. | |
**/ | |
BOOLEAN | |
ValidateHiiHandle ( | |
EFI_HII_HANDLE HiiHandle | |
) | |
{ | |
EFI_HII_HANDLE *HiiHandles; | |
UINTN Index; | |
BOOLEAN Find; | |
if (HiiHandle == NULL) { | |
return FALSE; | |
} | |
Find = FALSE; | |
HiiHandles = HiiGetHiiHandles (NULL); | |
ASSERT (HiiHandles != NULL); | |
for (Index = 0; HiiHandles[Index] != NULL; Index++) { | |
if (HiiHandles[Index] == HiiHandle) { | |
Find = TRUE; | |
break; | |
} | |
} | |
FreePool (HiiHandles); | |
return Find; | |
} | |
/** | |
Validate the FormSet. If the formset is not validate, remove it from the list. | |
@param FormSet The input FormSet which need to validate. | |
@retval TRUE The handle is validate. | |
@retval FALSE The handle is invalidate. | |
**/ | |
BOOLEAN | |
ValidateFormSet ( | |
FORM_BROWSER_FORMSET *FormSet | |
) | |
{ | |
BOOLEAN Find; | |
ASSERT (FormSet != NULL); | |
Find = ValidateHiiHandle(FormSet->HiiHandle); | |
// | |
// Should not remove the formset which is being used. | |
// | |
if (!Find && (FormSet != gCurrentSelection->FormSet)) { | |
CleanBrowserStorage(FormSet); | |
RemoveEntryList (&FormSet->Link); | |
DestroyFormSet (FormSet); | |
} | |
return Find; | |
} | |
/** | |
Check whether need to enable the reset flag in form level. | |
Also clean all ValueChanged flag in question. | |
@param SetFlag Whether need to set the Reset Flag. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
**/ | |
VOID | |
UpdateFlagForForm ( | |
IN BOOLEAN SetFlag, | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_BROWSER_STATEMENT *Question; | |
BOOLEAN OldValue; | |
Link = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, Link)) { | |
Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); | |
Link = GetNextNode (&Form->StatementListHead, Link); | |
if (!Question->ValueChanged) { | |
continue; | |
} | |
OldValue = Question->ValueChanged; | |
// | |
// Compare the buffer and editbuffer data to see whether the data has been saved. | |
// | |
Question->ValueChanged = IsQuestionValueChanged(FormSet, Form, Question, GetSetValueWithBothBuffer); | |
// | |
// Only the changed data has been saved, then need to set the reset flag. | |
// | |
if (SetFlag && OldValue && !Question->ValueChanged && ((Question->QuestionFlags & EFI_IFR_FLAG_RESET_REQUIRED) != 0)) { | |
gResetRequired = TRUE; | |
} | |
} | |
} | |
/** | |
Check whether need to enable the reset flag. | |
Also clean ValueChanged flag for all statements. | |
Form level or formset level, only one. | |
@param SetFlag Whether need to set the Reset Flag. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
**/ | |
VOID | |
ValueChangeResetFlagUpdate ( | |
IN BOOLEAN SetFlag, | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form | |
) | |
{ | |
FORM_BROWSER_FORM *CurrentForm; | |
LIST_ENTRY *Link; | |
if (Form != NULL) { | |
UpdateFlagForForm(SetFlag, FormSet, Form); | |
return; | |
} | |
Link = GetFirstNode (&FormSet->FormListHead); | |
while (!IsNull (&FormSet->FormListHead, Link)) { | |
CurrentForm = FORM_BROWSER_FORM_FROM_LINK (Link); | |
Link = GetNextNode (&FormSet->FormListHead, Link); | |
UpdateFlagForForm(SetFlag, FormSet, CurrentForm); | |
} | |
} | |
/** | |
Base on the return Progress string to find the form. | |
Base on the first return Offset/Width (Name) string to find the form | |
which keep this string. | |
@param FormSet FormSet data structure. | |
@param Storage Storage which has this Progress string. | |
@param Progress The Progress string which has the first fail string. | |
@param RetForm The return form for this progress string. | |
@param RetQuestion The return question for the error progress string. | |
@retval TRUE Find the error form and statement for this error progress string. | |
@retval FALSE Not find the error form. | |
**/ | |
BOOLEAN | |
FindQuestionFromProgress ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN BROWSER_STORAGE *Storage, | |
IN EFI_STRING Progress, | |
OUT FORM_BROWSER_FORM **RetForm, | |
OUT FORM_BROWSER_STATEMENT **RetQuestion | |
) | |
{ | |
LIST_ENTRY *Link; | |
LIST_ENTRY *LinkStorage; | |
LIST_ENTRY *LinkStatement; | |
FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; | |
FORM_BROWSER_FORM *Form; | |
EFI_STRING EndStr; | |
FORM_BROWSER_STATEMENT *Statement; | |
ASSERT ((*Progress == '&') || (*Progress == 'G')); | |
ConfigInfo = NULL; | |
*RetForm = NULL; | |
*RetQuestion = NULL; | |
// | |
// Skip the first "&" or the ConfigHdr part. | |
// | |
if (*Progress == '&') { | |
Progress++; | |
} else { | |
// | |
// Prepare the "NAME" or "OFFSET=0x####&WIDTH=0x####" string. | |
// | |
if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
// | |
// For Name/Value type, Skip the ConfigHdr part. | |
// | |
EndStr = StrStr (Progress, L"PATH="); | |
ASSERT (EndStr != NULL); | |
while (*EndStr != '&') { | |
EndStr++; | |
} | |
*EndStr = '\0'; | |
} else { | |
// | |
// For Buffer type, Skip the ConfigHdr part. | |
// | |
EndStr = StrStr (Progress, L"&OFFSET="); | |
ASSERT (EndStr != NULL); | |
*EndStr = '\0'; | |
} | |
Progress = EndStr + 1; | |
} | |
// | |
// Prepare the "NAME" or "OFFSET=0x####&WIDTH=0x####" string. | |
// | |
if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
// | |
// For Name/Value type, the data is "&Fred=16&George=16&Ron=12" formset, | |
// here, just keep the "Fred" string. | |
// | |
EndStr = StrStr (Progress, L"="); | |
ASSERT (EndStr != NULL); | |
*EndStr = '\0'; | |
} else { | |
// | |
// For Buffer type, the data is "OFFSET=0x####&WIDTH=0x####&VALUE=0x####", | |
// here, just keep the "OFFSET=0x####&WIDTH=0x####" string. | |
// | |
EndStr = StrStr (Progress, L"&VALUE="); | |
ASSERT (EndStr != NULL); | |
*EndStr = '\0'; | |
} | |
// | |
// Search in the form list. | |
// | |
Link = GetFirstNode (&FormSet->FormListHead); | |
while (!IsNull (&FormSet->FormListHead, Link)) { | |
Form = FORM_BROWSER_FORM_FROM_LINK (Link); | |
Link = GetNextNode (&FormSet->FormListHead, Link); | |
// | |
// Search in the ConfigReqeust list in this form. | |
// | |
LinkStorage = GetFirstNode (&Form->ConfigRequestHead); | |
while (!IsNull (&Form->ConfigRequestHead, LinkStorage)) { | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (LinkStorage); | |
LinkStorage = GetNextNode (&Form->ConfigRequestHead, LinkStorage); | |
if (Storage != ConfigInfo->Storage) { | |
continue; | |
} | |
if (StrStr (ConfigInfo->ConfigRequest, Progress) != NULL) { | |
// | |
// Find the OffsetWidth string in this form. | |
// | |
*RetForm = Form; | |
break; | |
} | |
} | |
if (*RetForm != NULL) { | |
LinkStatement = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, LinkStatement)) { | |
Statement = FORM_BROWSER_STATEMENT_FROM_LINK (LinkStatement); | |
LinkStatement = GetNextNode (&Form->StatementListHead, LinkStatement); | |
if (Statement->BlockName != NULL && StrStr (Statement->BlockName, Progress) != NULL) { | |
*RetQuestion = Statement; | |
break; | |
} | |
if (Statement->VariableName != NULL && StrStr (Statement->VariableName, Progress) != NULL) { | |
*RetQuestion = Statement; | |
break; | |
} | |
} | |
} | |
if (*RetForm != NULL) { | |
break; | |
} | |
} | |
// | |
// restore the OffsetWidth string to the original format. | |
// | |
if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
*EndStr = '='; | |
} else { | |
*EndStr = '&'; | |
} | |
return (BOOLEAN) (*RetForm != NULL); | |
} | |
/** | |
Popup an save error info and get user input. | |
@param TitleId The form title id. | |
@param HiiHandle The hii handle for this package. | |
@retval UINT32 The user select option for the save fail. | |
BROWSER_ACTION_DISCARD or BROWSER_ACTION_JUMP_TO_FORMSET | |
**/ | |
UINT32 | |
ConfirmSaveFail ( | |
IN EFI_STRING_ID TitleId, | |
IN EFI_HII_HANDLE HiiHandle | |
) | |
{ | |
CHAR16 *FormTitle; | |
CHAR16 *StringBuffer; | |
UINT32 RetVal; | |
FormTitle = GetToken (TitleId, HiiHandle); | |
StringBuffer = AllocateZeroPool (256 * sizeof (CHAR16)); | |
ASSERT (StringBuffer != NULL); | |
UnicodeSPrint ( | |
StringBuffer, | |
24 * sizeof (CHAR16) + StrSize (FormTitle), | |
L"Submit Fail For Form: %s.", | |
FormTitle | |
); | |
RetVal = PopupErrorMessage(BROWSER_SUBMIT_FAIL, NULL, NULL, StringBuffer); | |
FreePool (StringBuffer); | |
FreePool (FormTitle); | |
return RetVal; | |
} | |
/** | |
Popup an NO_SUBMIT_IF error info and get user input. | |
@param TitleId The form title id. | |
@param HiiHandle The hii handle for this package. | |
@retval UINT32 The user select option for the save fail. | |
BROWSER_ACTION_DISCARD or BROWSER_ACTION_JUMP_TO_FORMSET | |
**/ | |
UINT32 | |
ConfirmNoSubmitFail ( | |
IN EFI_STRING_ID TitleId, | |
IN EFI_HII_HANDLE HiiHandle | |
) | |
{ | |
CHAR16 *FormTitle; | |
CHAR16 *StringBuffer; | |
UINT32 RetVal; | |
FormTitle = GetToken (TitleId, HiiHandle); | |
StringBuffer = AllocateZeroPool (256 * sizeof (CHAR16)); | |
ASSERT (StringBuffer != NULL); | |
UnicodeSPrint ( | |
StringBuffer, | |
24 * sizeof (CHAR16) + StrSize (FormTitle), | |
L"NO_SUBMIT_IF error For Form: %s.", | |
FormTitle | |
); | |
RetVal = PopupErrorMessage(BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF, NULL, NULL, StringBuffer); | |
FreePool (StringBuffer); | |
FreePool (FormTitle); | |
return RetVal; | |
} | |
/** | |
Discard data based on the input setting scope (Form, FormSet or System). | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param SettingScope Setting Scope for Discard action. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_UNSUPPORTED Unsupport SettingScope. | |
**/ | |
EFI_STATUS | |
DiscardForm ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN BROWSER_SETTING_SCOPE SettingScope | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORMSET_STORAGE *Storage; | |
FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; | |
FORM_BROWSER_FORMSET *LocalFormSet; | |
FORM_BROWSER_FORMSET *OldFormSet; | |
// | |
// Check the supported setting level. | |
// | |
if (SettingScope >= MaxLevel) { | |
return EFI_UNSUPPORTED; | |
} | |
if (SettingScope == FormLevel && IsNvUpdateRequiredForForm (Form)) { | |
ConfigInfo = NULL; | |
Link = GetFirstNode (&Form->ConfigRequestHead); | |
while (!IsNull (&Form->ConfigRequestHead, Link)) { | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); | |
Link = GetNextNode (&Form->ConfigRequestHead, Link); | |
if (ConfigInfo->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
continue; | |
} | |
// | |
// Skip if there is no RequestElement | |
// | |
if (ConfigInfo->ElementCount == 0) { | |
continue; | |
} | |
// | |
// Prepare <ConfigResp> | |
// | |
SynchronizeStorage(ConfigInfo->Storage, ConfigInfo->ConfigRequest, FALSE); | |
// | |
// Call callback with Changed type to inform the driver. | |
// | |
SendDiscardInfoToDriver (FormSet, Form); | |
} | |
ValueChangeResetFlagUpdate (FALSE, FormSet, Form); | |
} else if (SettingScope == FormSetLevel && IsNvUpdateRequiredForFormSet (FormSet)) { | |
// | |
// Discard Buffer storage or Name/Value storage | |
// | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
Storage = FORMSET_STORAGE_FROM_LINK (Link); | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
continue; | |
} | |
// | |
// Skip if there is no RequestElement | |
// | |
if (Storage->ElementCount == 0) { | |
continue; | |
} | |
SynchronizeStorage(Storage->BrowserStorage, Storage->ConfigRequest, FALSE); | |
} | |
Link = GetFirstNode (&FormSet->FormListHead); | |
while (!IsNull (&FormSet->FormListHead, Link)) { | |
Form = FORM_BROWSER_FORM_FROM_LINK (Link); | |
Link = GetNextNode (&FormSet->FormListHead, Link); | |
// | |
// Call callback with Changed type to inform the driver. | |
// | |
SendDiscardInfoToDriver (FormSet, Form); | |
} | |
ValueChangeResetFlagUpdate(FALSE, FormSet, NULL); | |
} else if (SettingScope == SystemLevel) { | |
// | |
// System Level Discard. | |
// | |
OldFormSet = mSystemLevelFormSet; | |
// | |
// Discard changed value for each FormSet in the maintain list. | |
// | |
Link = GetFirstNode (&gBrowserFormSetList); | |
while (!IsNull (&gBrowserFormSetList, Link)) { | |
LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); | |
Link = GetNextNode (&gBrowserFormSetList, Link); | |
if (!ValidateFormSet(LocalFormSet)) { | |
continue; | |
} | |
mSystemLevelFormSet = LocalFormSet; | |
DiscardForm (LocalFormSet, NULL, FormSetLevel); | |
if (!IsHiiHandleInBrowserContext (LocalFormSet->HiiHandle)) { | |
// | |
// Remove maintain backup list after discard except for the current using FormSet. | |
// | |
CleanBrowserStorage(LocalFormSet); | |
RemoveEntryList (&LocalFormSet->Link); | |
DestroyFormSet (LocalFormSet); | |
} | |
} | |
mSystemLevelFormSet = OldFormSet; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Submit data for a form. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_UNSUPPORTED Unsupport SettingScope. | |
**/ | |
EFI_STATUS | |
SubmitForForm ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
EFI_STRING ConfigResp; | |
EFI_STRING Progress; | |
BROWSER_STORAGE *Storage; | |
FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; | |
if (!IsNvUpdateRequiredForForm (Form)) { | |
return EFI_SUCCESS; | |
} | |
Status = NoSubmitCheck (FormSet, &Form, NULL); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Link = GetFirstNode (&Form->ConfigRequestHead); | |
while (!IsNull (&Form->ConfigRequestHead, Link)) { | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); | |
Link = GetNextNode (&Form->ConfigRequestHead, Link); | |
Storage = ConfigInfo->Storage; | |
if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
continue; | |
} | |
// | |
// Skip if there is no RequestElement | |
// | |
if (ConfigInfo->ElementCount == 0) { | |
continue; | |
} | |
// | |
// 1. Prepare <ConfigResp> | |
// | |
Status = StorageToConfigResp (ConfigInfo->Storage, &ConfigResp, ConfigInfo->ConfigRequest, TRUE); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// 2. Set value to hii config routine protocol. | |
// | |
Status = mHiiConfigRouting->RouteConfig ( | |
mHiiConfigRouting, | |
ConfigResp, | |
&Progress | |
); | |
FreePool (ConfigResp); | |
if (EFI_ERROR (Status)) { | |
InsertTailList (&gBrowserSaveFailFormSetList, &ConfigInfo->SaveFailLink); | |
continue; | |
} | |
// | |
// 3. Config success, update storage shadow Buffer, only update the data belong to this form. | |
// | |
SynchronizeStorage (ConfigInfo->Storage, ConfigInfo->ConfigRequest, TRUE); | |
} | |
// | |
// 4. Process the save failed storage. | |
// | |
if (!IsListEmpty (&gBrowserSaveFailFormSetList)) { | |
if (ConfirmSaveFail (Form->FormTitle, FormSet->HiiHandle) == BROWSER_ACTION_DISCARD) { | |
Link = GetFirstNode (&gBrowserSaveFailFormSetList); | |
while (!IsNull (&gBrowserSaveFailFormSetList, Link)) { | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_SAVE_FAIL_LINK (Link); | |
Link = GetNextNode (&gBrowserSaveFailFormSetList, Link); | |
SynchronizeStorage(ConfigInfo->Storage, ConfigInfo->ConfigRequest, FALSE); | |
Status = EFI_SUCCESS; | |
} | |
} else { | |
Status = EFI_UNSUPPORTED; | |
} | |
// | |
// Free Form save fail list. | |
// | |
while (!IsListEmpty (&gBrowserSaveFailFormSetList)) { | |
Link = GetFirstNode (&gBrowserSaveFailFormSetList); | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_SAVE_FAIL_LINK (Link); | |
RemoveEntryList (&ConfigInfo->SaveFailLink); | |
} | |
} | |
// | |
// 5. Update the NV flag. | |
// | |
ValueChangeResetFlagUpdate(TRUE, FormSet, Form); | |
return Status; | |
} | |
/** | |
Submit data for a formset. | |
@param FormSet FormSet data structure. | |
@param SkipProcessFail Whether skip to process the save failed storage. | |
If submit formset is called when do system level save, | |
set this value to true and process the failed formset | |
together. | |
if submit formset is called when do formset level save, | |
set the value to false and process the failed storage | |
right after process all storages for this formset. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_UNSUPPORTED Unsupport SettingScope. | |
**/ | |
EFI_STATUS | |
SubmitForFormSet ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN BOOLEAN SkipProcessFail | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
EFI_STRING ConfigResp; | |
EFI_STRING Progress; | |
BROWSER_STORAGE *Storage; | |
FORMSET_STORAGE *FormSetStorage; | |
FORM_BROWSER_FORM *Form; | |
BOOLEAN HasInserted; | |
FORM_BROWSER_STATEMENT *Question; | |
HasInserted = FALSE; | |
if (!IsNvUpdateRequiredForFormSet (FormSet)) { | |
return EFI_SUCCESS; | |
} | |
Form = NULL; | |
Status = NoSubmitCheck (FormSet, &Form, &Question); | |
if (EFI_ERROR (Status)) { | |
if (SkipProcessFail) { | |
// | |
// Process NO_SUBMIT check first, so insert it at head. | |
// | |
FormSet->SaveFailForm = Form; | |
FormSet->SaveFailStatement = Question; | |
InsertHeadList (&gBrowserSaveFailFormSetList, &FormSet->SaveFailLink); | |
} | |
return Status; | |
} | |
Form = NULL; | |
Question = NULL; | |
// | |
// Submit Buffer storage or Name/Value storage | |
// | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); | |
Storage = FormSetStorage->BrowserStorage; | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
continue; | |
} | |
// | |
// Skip if there is no RequestElement | |
// | |
if (FormSetStorage->ElementCount == 0) { | |
continue; | |
} | |
// | |
// 1. Prepare <ConfigResp> | |
// | |
Status = StorageToConfigResp (Storage, &ConfigResp, FormSetStorage->ConfigRequest, TRUE); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// 2. Send <ConfigResp> to Routine config Protocol. | |
// | |
Status = mHiiConfigRouting->RouteConfig ( | |
mHiiConfigRouting, | |
ConfigResp, | |
&Progress | |
); | |
if (EFI_ERROR (Status)) { | |
InsertTailList (&FormSet->SaveFailStorageListHead, &FormSetStorage->SaveFailLink); | |
if (!HasInserted) { | |
// | |
// Call submit formset for system level, save the formset info | |
// and process later. | |
// | |
FindQuestionFromProgress(FormSet, Storage, Progress, &Form, &Question); | |
ASSERT (Form != NULL && Question != NULL); | |
FormSet->SaveFailForm = Form; | |
FormSet->SaveFailStatement = Question; | |
if (SkipProcessFail) { | |
InsertTailList (&gBrowserSaveFailFormSetList, &FormSet->SaveFailLink); | |
} | |
HasInserted = TRUE; | |
} | |
FreePool (ConfigResp); | |
continue; | |
} | |
FreePool (ConfigResp); | |
// | |
// 3. Config success, update storage shadow Buffer | |
// | |
SynchronizeStorage (Storage, FormSetStorage->ConfigRequest, TRUE); | |
} | |
// | |
// 4. Has save fail storage need to handle. | |
// | |
if (Form != NULL) { | |
if (!SkipProcessFail) { | |
// | |
// If not in system level, just handl the save failed storage here. | |
// | |
if (ConfirmSaveFail (Form->FormTitle, FormSet->HiiHandle) == BROWSER_ACTION_DISCARD) { | |
Link = GetFirstNode (&FormSet->SaveFailStorageListHead); | |
while (!IsNull (&FormSet->SaveFailStorageListHead, Link)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (Link); | |
Storage = FormSetStorage->BrowserStorage; | |
Link = GetNextNode (&FormSet->SaveFailStorageListHead, Link); | |
SynchronizeStorage(FormSetStorage->BrowserStorage, FormSetStorage->ConfigRequest, FALSE); | |
Status = EFI_SUCCESS; | |
} | |
} else { | |
UiCopyMenuList(&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &Form->FormViewListHead); | |
gCurrentSelection->Action = UI_ACTION_REFRESH_FORMSET; | |
gCurrentSelection->Handle = FormSet->HiiHandle; | |
CopyGuid (&gCurrentSelection->FormSetGuid, &FormSet->Guid); | |
gCurrentSelection->FormId = Form->FormId; | |
gCurrentSelection->QuestionId = Question->QuestionId; | |
Status = EFI_UNSUPPORTED; | |
} | |
// | |
// Free FormSet save fail list. | |
// | |
while (!IsListEmpty (&FormSet->SaveFailStorageListHead)) { | |
Link = GetFirstNode (&FormSet->SaveFailStorageListHead); | |
FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (Link); | |
RemoveEntryList (&FormSetStorage->SaveFailLink); | |
} | |
} else { | |
// | |
// If in system level, just return error and handle the failed formset later. | |
// | |
Status = EFI_UNSUPPORTED; | |
} | |
} | |
// | |
// 5. Update the NV flag. | |
// | |
ValueChangeResetFlagUpdate(TRUE, FormSet, NULL); | |
return Status; | |
} | |
/** | |
Submit data for all formsets. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_UNSUPPORTED Unsupport SettingScope. | |
**/ | |
EFI_STATUS | |
SubmitForSystem ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
LIST_ENTRY *StorageLink; | |
FORMSET_STORAGE *FormSetStorage; | |
FORM_BROWSER_FORM *Form; | |
FORM_BROWSER_FORMSET *LocalFormSet; | |
UINT32 UserSelection; | |
FORM_BROWSER_STATEMENT *Question; | |
mSystemSubmit = TRUE; | |
Link = GetFirstNode (&gBrowserFormSetList); | |
while (!IsNull (&gBrowserFormSetList, Link)) { | |
LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); | |
Link = GetNextNode (&gBrowserFormSetList, Link); | |
if (!ValidateFormSet(LocalFormSet)) { | |
continue; | |
} | |
Status = SubmitForFormSet (LocalFormSet, TRUE); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// | |
// Remove maintain backup list after save except for the current using FormSet. | |
// | |
if (!IsHiiHandleInBrowserContext (LocalFormSet->HiiHandle)) { | |
CleanBrowserStorage(LocalFormSet); | |
RemoveEntryList (&LocalFormSet->Link); | |
DestroyFormSet (LocalFormSet); | |
} | |
} | |
mSystemSubmit = FALSE; | |
Status = EFI_SUCCESS; | |
// | |
// Process the save failed formsets. | |
// | |
Link = GetFirstNode (&gBrowserSaveFailFormSetList); | |
while (!IsNull (&gBrowserSaveFailFormSetList, Link)) { | |
LocalFormSet = FORM_BROWSER_FORMSET_FROM_SAVE_FAIL_LINK (Link); | |
Link = GetNextNode (&gBrowserSaveFailFormSetList, Link); | |
if (!ValidateFormSet(LocalFormSet)) { | |
continue; | |
} | |
Form = LocalFormSet->SaveFailForm; | |
Question= LocalFormSet->SaveFailStatement; | |
// | |
// Confirm with user, get user input. | |
// | |
if (IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { | |
// | |
// NULL for SaveFailStorageListHead means error caused by NO_SUBMIT_IF check. | |
// | |
UserSelection = ConfirmNoSubmitFail (Form->FormTitle, LocalFormSet->HiiHandle); | |
} else { | |
UserSelection = ConfirmSaveFail (Form->FormTitle, LocalFormSet->HiiHandle); | |
} | |
if (UserSelection == BROWSER_ACTION_DISCARD) { | |
if (IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { | |
StorageLink = GetFirstNode (&LocalFormSet->StorageListHead); | |
while (!IsNull (&LocalFormSet->StorageListHead, StorageLink)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_LINK (StorageLink); | |
StorageLink = GetNextNode (&LocalFormSet->StorageListHead, StorageLink); | |
SynchronizeStorage(FormSetStorage->BrowserStorage, FormSetStorage->ConfigRequest, FALSE); | |
} | |
} else { | |
StorageLink = GetFirstNode (&LocalFormSet->SaveFailStorageListHead); | |
while (!IsNull (&LocalFormSet->SaveFailStorageListHead, StorageLink)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (StorageLink); | |
StorageLink = GetNextNode (&LocalFormSet->SaveFailStorageListHead, StorageLink); | |
SynchronizeStorage(FormSetStorage->BrowserStorage, FormSetStorage->ConfigRequest, FALSE); | |
} | |
} | |
if (!IsHiiHandleInBrowserContext (LocalFormSet->HiiHandle)) { | |
CleanBrowserStorage(LocalFormSet); | |
RemoveEntryList (&LocalFormSet->Link); | |
RemoveEntryList (&LocalFormSet->SaveFailLink); | |
DestroyFormSet (LocalFormSet); | |
} else { | |
ValueChangeResetFlagUpdate(FALSE, LocalFormSet, NULL); | |
} | |
} else { | |
if (IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { | |
NoSubmitCheck (LocalFormSet, &Form, &Question); | |
} | |
UiCopyMenuList(&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &Form->FormViewListHead); | |
gCurrentSelection->Action = UI_ACTION_REFRESH_FORMSET; | |
gCurrentSelection->Handle = LocalFormSet->HiiHandle; | |
CopyGuid (&gCurrentSelection->FormSetGuid, &LocalFormSet->Guid); | |
gCurrentSelection->FormId = Form->FormId; | |
gCurrentSelection->QuestionId = Question->QuestionId; | |
Status = EFI_UNSUPPORTED; | |
break; | |
} | |
} | |
// | |
// Clean the list which will not process. | |
// | |
while (!IsListEmpty (&gBrowserSaveFailFormSetList)) { | |
Link = GetFirstNode (&gBrowserSaveFailFormSetList); | |
LocalFormSet = FORM_BROWSER_FORMSET_FROM_SAVE_FAIL_LINK (Link); | |
RemoveEntryList (&LocalFormSet->SaveFailLink); | |
while (!IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { | |
StorageLink = GetFirstNode (&LocalFormSet->SaveFailStorageListHead); | |
FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (StorageLink); | |
RemoveEntryList (&FormSetStorage->SaveFailLink); | |
} | |
} | |
return Status; | |
} | |
/** | |
Submit data based on the input Setting level (Form, FormSet or System). | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param SettingScope Setting Scope for Submit action. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_UNSUPPORTED Unsupport SettingScope. | |
**/ | |
EFI_STATUS | |
SubmitForm ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN BROWSER_SETTING_SCOPE SettingScope | |
) | |
{ | |
EFI_STATUS Status; | |
switch (SettingScope) { | |
case FormLevel: | |
Status = SubmitForForm(FormSet, Form); | |
break; | |
case FormSetLevel: | |
Status = SubmitForFormSet (FormSet, FALSE); | |
break; | |
case SystemLevel: | |
Status = SubmitForSystem (); | |
break; | |
default: | |
Status = EFI_UNSUPPORTED; | |
break; | |
} | |
return Status; | |
} | |
/** | |
Converts the unicode character of the string from uppercase to lowercase. | |
This is a internal function. | |
@param ConfigString String to be converted | |
**/ | |
VOID | |
EFIAPI | |
HiiToLower ( | |
IN EFI_STRING ConfigString | |
) | |
{ | |
EFI_STRING String; | |
BOOLEAN Lower; | |
ASSERT (ConfigString != NULL); | |
// | |
// Convert all hex digits in range [A-F] in the configuration header to [a-f] | |
// | |
for (String = ConfigString, Lower = FALSE; *String != L'\0'; String++) { | |
if (*String == L'=') { | |
Lower = TRUE; | |
} else if (*String == L'&') { | |
Lower = FALSE; | |
} else if (Lower && *String >= L'A' && *String <= L'F') { | |
*String = (CHAR16) (*String - L'A' + L'a'); | |
} | |
} | |
} | |
/** | |
Find the point in the ConfigResp string for this question. | |
@param Question The question. | |
@param ConfigResp Get ConfigResp string. | |
@retval point to the offset where is for this question. | |
**/ | |
CHAR16 * | |
GetOffsetFromConfigResp ( | |
IN FORM_BROWSER_STATEMENT *Question, | |
IN CHAR16 *ConfigResp | |
) | |
{ | |
CHAR16 *RequestElement; | |
CHAR16 *BlockData; | |
// | |
// Type is EFI_HII_VARSTORE_NAME_VALUE. | |
// | |
if (Question->Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
RequestElement = StrStr (ConfigResp, Question->VariableName); | |
if (RequestElement != NULL) { | |
// | |
// Skip the "VariableName=" field. | |
// | |
RequestElement += StrLen (Question->VariableName) + 1; | |
} | |
return RequestElement; | |
} | |
// | |
// Type is EFI_HII_VARSTORE_EFI_VARIABLE or EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER | |
// | |
// | |
// 1. Directly use Question->BlockName to find. | |
// | |
RequestElement = StrStr (ConfigResp, Question->BlockName); | |
if (RequestElement != NULL) { | |
// | |
// Skip the "Question->BlockName&VALUE=" field. | |
// | |
RequestElement += StrLen (Question->BlockName) + StrLen (L"&VALUE="); | |
return RequestElement; | |
} | |
// | |
// 2. Change all hex digits in Question->BlockName to lower and compare again. | |
// | |
BlockData = AllocateCopyPool (StrSize(Question->BlockName), Question->BlockName); | |
ASSERT (BlockData != NULL); | |
HiiToLower (BlockData); | |
RequestElement = StrStr (ConfigResp, BlockData); | |
FreePool (BlockData); | |
if (RequestElement != NULL) { | |
// | |
// Skip the "Question->BlockName&VALUE=" field. | |
// | |
RequestElement += StrLen (Question->BlockName) + StrLen (L"&VALUE="); | |
} | |
return RequestElement; | |
} | |
/** | |
Get Question default value from AltCfg string. | |
@param FormSet The form set. | |
@param Form The form | |
@param Question The question. | |
@retval EFI_SUCCESS Question is reset to default value. | |
**/ | |
EFI_STATUS | |
GetDefaultValueFromAltCfg ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN OUT FORM_BROWSER_STATEMENT *Question | |
) | |
{ | |
BROWSER_STORAGE *Storage; | |
FORMSET_STORAGE *FormSetStorage; | |
CHAR16 *ConfigResp; | |
CHAR16 *Value; | |
LIST_ENTRY *Link; | |
FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; | |
Storage = Question->Storage; | |
if ((Storage == NULL) || (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE)) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Try to get AltCfg string from form. If not found it, then | |
// try to get it from formset. | |
// | |
ConfigResp = NULL; | |
Link = GetFirstNode (&Form->ConfigRequestHead); | |
while (!IsNull (&Form->ConfigRequestHead, Link)) { | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); | |
Link = GetNextNode (&Form->ConfigRequestHead, Link); | |
if (Storage == ConfigInfo->Storage) { | |
ConfigResp = ConfigInfo->ConfigAltResp; | |
break; | |
} | |
} | |
if (ConfigResp == NULL) { | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
if (Storage == FormSetStorage->BrowserStorage) { | |
ConfigResp = FormSetStorage->ConfigAltResp; | |
break; | |
} | |
} | |
} | |
if (ConfigResp == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
Value = GetOffsetFromConfigResp (Question, ConfigResp); | |
if (Value == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
return BufferToValue (Question, Value); | |
} | |
/** | |
Get default Id value used for browser. | |
@param DefaultId The default id value used by hii. | |
@retval Browser used default value. | |
**/ | |
INTN | |
GetDefaultIdForCallBack ( | |
UINTN DefaultId | |
) | |
{ | |
if (DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) { | |
return EFI_BROWSER_ACTION_DEFAULT_STANDARD; | |
} else if (DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) { | |
return EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING; | |
} else if (DefaultId == EFI_HII_DEFAULT_CLASS_SAFE) { | |
return EFI_BROWSER_ACTION_DEFAULT_SAFE; | |
} else if (DefaultId >= EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN + 0x1000) { | |
return EFI_BROWSER_ACTION_DEFAULT_PLATFORM + DefaultId - EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN; | |
} else if (DefaultId >= EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN + 0x1000) { | |
return EFI_BROWSER_ACTION_DEFAULT_HARDWARE + DefaultId - EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN; | |
} else if (DefaultId >= EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN + 0x1000) { | |
return EFI_BROWSER_ACTION_DEFAULT_FIRMWARE + DefaultId - EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN; | |
} else { | |
return -1; | |
} | |
} | |
/** | |
Return data element in an Array by its Index. | |
@param Array The data array. | |
@param Type Type of the data in this array. | |
@param Index Zero based index for data in this array. | |
@retval Value The data to be returned | |
**/ | |
UINT64 | |
GetArrayData ( | |
IN VOID *Array, | |
IN UINT8 Type, | |
IN UINTN Index | |
) | |
{ | |
UINT64 Data; | |
ASSERT (Array != NULL); | |
Data = 0; | |
switch (Type) { | |
case EFI_IFR_TYPE_NUM_SIZE_8: | |
Data = (UINT64) *(((UINT8 *) Array) + Index); | |
break; | |
case EFI_IFR_TYPE_NUM_SIZE_16: | |
Data = (UINT64) *(((UINT16 *) Array) + Index); | |
break; | |
case EFI_IFR_TYPE_NUM_SIZE_32: | |
Data = (UINT64) *(((UINT32 *) Array) + Index); | |
break; | |
case EFI_IFR_TYPE_NUM_SIZE_64: | |
Data = (UINT64) *(((UINT64 *) Array) + Index); | |
break; | |
default: | |
break; | |
} | |
return Data; | |
} | |
/** | |
Set value of a data element in an Array by its Index. | |
@param Array The data array. | |
@param Type Type of the data in this array. | |
@param Index Zero based index for data in this array. | |
@param Value The value to be set. | |
**/ | |
VOID | |
SetArrayData ( | |
IN VOID *Array, | |
IN UINT8 Type, | |
IN UINTN Index, | |
IN UINT64 Value | |
) | |
{ | |
ASSERT (Array != NULL); | |
switch (Type) { | |
case EFI_IFR_TYPE_NUM_SIZE_8: | |
*(((UINT8 *) Array) + Index) = (UINT8) Value; | |
break; | |
case EFI_IFR_TYPE_NUM_SIZE_16: | |
*(((UINT16 *) Array) + Index) = (UINT16) Value; | |
break; | |
case EFI_IFR_TYPE_NUM_SIZE_32: | |
*(((UINT32 *) Array) + Index) = (UINT32) Value; | |
break; | |
case EFI_IFR_TYPE_NUM_SIZE_64: | |
*(((UINT64 *) Array) + Index) = (UINT64) Value; | |
break; | |
default: | |
break; | |
} | |
} | |
/** | |
Search an Option of a Question by its value. | |
@param Question The Question | |
@param OptionValue Value for Option to be searched. | |
@retval Pointer Pointer to the found Option. | |
@retval NULL Option not found. | |
**/ | |
QUESTION_OPTION * | |
ValueToOption ( | |
IN FORM_BROWSER_STATEMENT *Question, | |
IN EFI_HII_VALUE *OptionValue | |
) | |
{ | |
LIST_ENTRY *Link; | |
QUESTION_OPTION *Option; | |
INTN Result; | |
Link = GetFirstNode (&Question->OptionListHead); | |
while (!IsNull (&Question->OptionListHead, Link)) { | |
Option = QUESTION_OPTION_FROM_LINK (Link); | |
if ((CompareHiiValue (&Option->Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { | |
// | |
// Check the suppressif condition, only a valid option can be return. | |
// | |
if ((Option->SuppressExpression == NULL) || | |
((EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse))) { | |
return Option; | |
} | |
} | |
Link = GetNextNode (&Question->OptionListHead, Link); | |
} | |
return NULL; | |
} | |
/** | |
Reset Question to its default value. | |
@param FormSet The form set. | |
@param Form The form. | |
@param Question The question. | |
@param DefaultId The Class of the default. | |
@retval EFI_SUCCESS Question is reset to default value. | |
**/ | |
EFI_STATUS | |
GetQuestionDefault ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN FORM_BROWSER_STATEMENT *Question, | |
IN UINT16 DefaultId | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
QUESTION_DEFAULT *Default; | |
QUESTION_OPTION *Option; | |
EFI_HII_VALUE *HiiValue; | |
UINT8 Index; | |
EFI_STRING StrValue; | |
EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; | |
EFI_BROWSER_ACTION_REQUEST ActionRequest; | |
INTN Action; | |
CHAR16 *NewString; | |
Status = EFI_NOT_FOUND; | |
StrValue = NULL; | |
// | |
// Statement don't have storage, skip them | |
// | |
if (Question->QuestionId == 0) { | |
return Status; | |
} | |
// | |
// There are Five ways to specify default value for a Question: | |
// 1, use call back function (highest priority) | |
// 2, use ExtractConfig function | |
// 3, use nested EFI_IFR_DEFAULT | |
// 4, set flags of EFI_ONE_OF_OPTION (provide Standard and Manufacturing default) | |
// 5, set flags of EFI_IFR_CHECKBOX (provide Standard and Manufacturing default) (lowest priority) | |
// | |
HiiValue = &Question->HiiValue; | |
// | |
// Get Question defaut value from call back function. | |
// | |
ConfigAccess = FormSet->ConfigAccess; | |
Action = GetDefaultIdForCallBack (DefaultId); | |
if ((Action > 0) && ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) && (ConfigAccess != NULL)) { | |
ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; | |
Status = ConfigAccess->Callback ( | |
ConfigAccess, | |
Action, | |
Question->QuestionId, | |
HiiValue->Type, | |
&HiiValue->Value, | |
&ActionRequest | |
); | |
if (!EFI_ERROR (Status)) { | |
if (HiiValue->Type == EFI_IFR_TYPE_STRING) { | |
NewString = GetToken (Question->HiiValue.Value.string, FormSet->HiiHandle); | |
ASSERT (NewString != NULL); | |
ASSERT (StrLen (NewString) * sizeof (CHAR16) <= Question->StorageWidth); | |
if (StrLen (NewString) * sizeof (CHAR16) <= Question->StorageWidth) { | |
CopyMem (Question->BufferValue, NewString, StrSize (NewString)); | |
} else { | |
CopyMem (Question->BufferValue, NewString, Question->StorageWidth); | |
} | |
FreePool (NewString); | |
} | |
return Status; | |
} | |
} | |
// | |
// Get default value from altcfg string. | |
// | |
if (ConfigAccess != NULL) { | |
Status = GetDefaultValueFromAltCfg(FormSet, Form, Question); | |
if (!EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
// | |
// EFI_IFR_DEFAULT has highest priority | |
// | |
if (!IsListEmpty (&Question->DefaultListHead)) { | |
Link = GetFirstNode (&Question->DefaultListHead); | |
while (!IsNull (&Question->DefaultListHead, Link)) { | |
Default = QUESTION_DEFAULT_FROM_LINK (Link); | |
if (Default->DefaultId == DefaultId) { | |
if (Default->ValueExpression != NULL) { | |
// | |
// Default is provided by an Expression, evaluate it | |
// | |
Status = EvaluateExpression (FormSet, Form, Default->ValueExpression); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Default->ValueExpression->Result.Type == EFI_IFR_TYPE_BUFFER) { | |
ASSERT (HiiValue->Type == EFI_IFR_TYPE_BUFFER && Question->BufferValue != NULL); | |
if (Question->StorageWidth > Default->ValueExpression->Result.BufferLen) { | |
CopyMem (Question->HiiValue.Buffer, Default->ValueExpression->Result.Buffer, Default->ValueExpression->Result.BufferLen); | |
Question->HiiValue.BufferLen = Default->ValueExpression->Result.BufferLen; | |
} else { | |
CopyMem (Question->HiiValue.Buffer, Default->ValueExpression->Result.Buffer, Question->StorageWidth); | |
Question->HiiValue.BufferLen = Question->StorageWidth; | |
} | |
FreePool (Default->ValueExpression->Result.Buffer); | |
} | |
HiiValue->Type = Default->ValueExpression->Result.Type; | |
CopyMem (&HiiValue->Value, &Default->ValueExpression->Result.Value, sizeof (EFI_IFR_TYPE_VALUE)); | |
} else { | |
// | |
// Default value is embedded in EFI_IFR_DEFAULT | |
// | |
CopyMem (HiiValue, &Default->Value, sizeof (EFI_HII_VALUE)); | |
} | |
if (HiiValue->Type == EFI_IFR_TYPE_STRING) { | |
StrValue = HiiGetString (FormSet->HiiHandle, HiiValue->Value.string, NULL); | |
if (StrValue == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
if (Question->StorageWidth > StrSize (StrValue)) { | |
CopyMem (Question->BufferValue, StrValue, StrSize (StrValue)); | |
} else { | |
CopyMem (Question->BufferValue, StrValue, Question->StorageWidth); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
Link = GetNextNode (&Question->DefaultListHead, Link); | |
} | |
} | |
// | |
// EFI_ONE_OF_OPTION | |
// | |
if ((Question->Operand == EFI_IFR_ONE_OF_OP) && !IsListEmpty (&Question->OptionListHead)) { | |
if (DefaultId <= EFI_HII_DEFAULT_CLASS_MANUFACTURING) { | |
// | |
// OneOfOption could only provide Standard and Manufacturing default | |
// | |
Link = GetFirstNode (&Question->OptionListHead); | |
while (!IsNull (&Question->OptionListHead, Link)) { | |
Option = QUESTION_OPTION_FROM_LINK (Link); | |
Link = GetNextNode (&Question->OptionListHead, Link); | |
if ((Option->SuppressExpression != NULL) && | |
EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) { | |
continue; | |
} | |
if (((DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) && ((Option->Flags & EFI_IFR_OPTION_DEFAULT) != 0)) || | |
((DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) && ((Option->Flags & EFI_IFR_OPTION_DEFAULT_MFG) != 0)) | |
) { | |
CopyMem (HiiValue, &Option->Value, sizeof (EFI_HII_VALUE)); | |
return EFI_SUCCESS; | |
} | |
} | |
} | |
} | |
// | |
// EFI_IFR_CHECKBOX - lowest priority | |
// | |
if (Question->Operand == EFI_IFR_CHECKBOX_OP) { | |
if (DefaultId <= EFI_HII_DEFAULT_CLASS_MANUFACTURING) { | |
// | |
// Checkbox could only provide Standard and Manufacturing default | |
// | |
if (((DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) && ((Question->Flags & EFI_IFR_CHECKBOX_DEFAULT) != 0)) || | |
((DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) && ((Question->Flags & EFI_IFR_CHECKBOX_DEFAULT_MFG) != 0)) | |
) { | |
HiiValue->Value.b = TRUE; | |
} else { | |
HiiValue->Value.b = FALSE; | |
} | |
return EFI_SUCCESS; | |
} | |
} | |
// | |
// For Questions without default | |
// | |
Status = EFI_NOT_FOUND; | |
switch (Question->Operand) { | |
case EFI_IFR_NUMERIC_OP: | |
// | |
// Take minimum value as numeric default value | |
// | |
if ((HiiValue->Value.u64 < Question->Minimum) || (HiiValue->Value.u64 > Question->Maximum)) { | |
HiiValue->Value.u64 = Question->Minimum; | |
Status = EFI_SUCCESS; | |
} | |
break; | |
case EFI_IFR_ONE_OF_OP: | |
// | |
// Take first oneof option as oneof's default value | |
// | |
if (ValueToOption (Question, HiiValue) == NULL) { | |
Link = GetFirstNode (&Question->OptionListHead); | |
while (!IsNull (&Question->OptionListHead, Link)) { | |
Option = QUESTION_OPTION_FROM_LINK (Link); | |
Link = GetNextNode (&Question->OptionListHead, Link); | |
if ((Option->SuppressExpression != NULL) && | |
EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) { | |
continue; | |
} | |
CopyMem (HiiValue, &Option->Value, sizeof (EFI_HII_VALUE)); | |
Status = EFI_SUCCESS; | |
break; | |
} | |
} | |
break; | |
case EFI_IFR_ORDERED_LIST_OP: | |
// | |
// Take option sequence in IFR as ordered list's default value | |
// | |
Index = 0; | |
Link = GetFirstNode (&Question->OptionListHead); | |
while (!IsNull (&Question->OptionListHead, Link)) { | |
Status = EFI_SUCCESS; | |
Option = QUESTION_OPTION_FROM_LINK (Link); | |
Link = GetNextNode (&Question->OptionListHead, Link); | |
if ((Option->SuppressExpression != NULL) && | |
EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) { | |
continue; | |
} | |
SetArrayData (Question->BufferValue, Question->ValueType, Index, Option->Value.Value.u64); | |
Index++; | |
if (Index >= Question->MaxContainers) { | |
break; | |
} | |
} | |
break; | |
default: | |
break; | |
} | |
return Status; | |
} | |
/** | |
Get AltCfg string for current form. | |
@param FormSet Form data structure. | |
@param Form Form data structure. | |
@param DefaultId The Class of the default. | |
@param BrowserStorage The input request storage for the questions. | |
**/ | |
VOID | |
ExtractAltCfgForForm ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN UINT16 DefaultId, | |
IN BROWSER_STORAGE *BrowserStorage | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
CHAR16 *ConfigResp; | |
CHAR16 *Progress; | |
CHAR16 *Result; | |
BROWSER_STORAGE *Storage; | |
FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; | |
FORMSET_STORAGE *FormSetStorage; | |
// | |
// Check whether has get AltCfg string for this formset. | |
// If yes, no need to get AltCfg for form. | |
// | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); | |
Storage = FormSetStorage->BrowserStorage; | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
if (BrowserStorage != NULL && BrowserStorage != Storage) { | |
continue; | |
} | |
if (Storage->Type != EFI_HII_VARSTORE_EFI_VARIABLE && | |
FormSetStorage->ElementCount != 0 && | |
FormSetStorage->HasCallAltCfg) { | |
return; | |
} | |
} | |
// | |
// Get AltCfg string for each form. | |
// | |
Link = GetFirstNode (&Form->ConfigRequestHead); | |
while (!IsNull (&Form->ConfigRequestHead, Link)) { | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); | |
Link = GetNextNode (&Form->ConfigRequestHead, Link); | |
Storage = ConfigInfo->Storage; | |
if (BrowserStorage != NULL && BrowserStorage != Storage) { | |
continue; | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
continue; | |
} | |
// | |
// 1. Skip if there is no RequestElement | |
// | |
if (ConfigInfo->ElementCount == 0) { | |
continue; | |
} | |
// | |
// 2. Get value through hii config routine protocol. | |
// | |
Status = mHiiConfigRouting->ExtractConfig ( | |
mHiiConfigRouting, | |
ConfigInfo->ConfigRequest, | |
&Progress, | |
&Result | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// | |
// 3. Call ConfigRouting GetAltCfg(ConfigRoute, <ConfigResponse>, Guid, Name, DevicePath, AltCfgId, AltCfgResp) | |
// Get the default configuration string according to the default ID. | |
// | |
Status = mHiiConfigRouting->GetAltConfig ( | |
mHiiConfigRouting, | |
Result, | |
&Storage->Guid, | |
Storage->Name, | |
NULL, | |
&DefaultId, // it can be NULL to get the current setting. | |
&ConfigResp | |
); | |
FreePool (Result); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
ConfigInfo->ConfigAltResp = ConfigResp; | |
} | |
} | |
/** | |
Clean AltCfg string for current form. | |
@param Form Form data structure. | |
**/ | |
VOID | |
CleanAltCfgForForm ( | |
IN FORM_BROWSER_FORM *Form | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; | |
Link = GetFirstNode (&Form->ConfigRequestHead); | |
while (!IsNull (&Form->ConfigRequestHead, Link)) { | |
ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); | |
Link = GetNextNode (&Form->ConfigRequestHead, Link); | |
if (ConfigInfo->ConfigAltResp != NULL) { | |
FreePool (ConfigInfo->ConfigAltResp); | |
ConfigInfo->ConfigAltResp = NULL; | |
} | |
} | |
} | |
/** | |
Get AltCfg string for current formset. | |
@param FormSet Form data structure. | |
@param DefaultId The Class of the default. | |
@param BrowserStorage The input request storage for the questions. | |
**/ | |
VOID | |
ExtractAltCfgForFormSet ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN UINT16 DefaultId, | |
IN BROWSER_STORAGE *BrowserStorage | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
CHAR16 *ConfigResp; | |
CHAR16 *Progress; | |
CHAR16 *Result; | |
BROWSER_STORAGE *Storage; | |
FORMSET_STORAGE *FormSetStorage; | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); | |
Storage = FormSetStorage->BrowserStorage; | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
if (BrowserStorage != NULL && BrowserStorage != Storage) { | |
continue; | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { | |
continue; | |
} | |
// | |
// 1. Skip if there is no RequestElement | |
// | |
if (FormSetStorage->ElementCount == 0) { | |
continue; | |
} | |
FormSetStorage->HasCallAltCfg = TRUE; | |
// | |
// 2. Get value through hii config routine protocol. | |
// | |
Status = mHiiConfigRouting->ExtractConfig ( | |
mHiiConfigRouting, | |
FormSetStorage->ConfigRequest, | |
&Progress, | |
&Result | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// | |
// 3. Call ConfigRouting GetAltCfg(ConfigRoute, <ConfigResponse>, Guid, Name, DevicePath, AltCfgId, AltCfgResp) | |
// Get the default configuration string according to the default ID. | |
// | |
Status = mHiiConfigRouting->GetAltConfig ( | |
mHiiConfigRouting, | |
Result, | |
&Storage->Guid, | |
Storage->Name, | |
NULL, | |
&DefaultId, // it can be NULL to get the current setting. | |
&ConfigResp | |
); | |
FreePool (Result); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
FormSetStorage->ConfigAltResp = ConfigResp; | |
} | |
} | |
/** | |
Clean AltCfg string for current formset. | |
@param FormSet Form data structure. | |
**/ | |
VOID | |
CleanAltCfgForFormSet ( | |
IN FORM_BROWSER_FORMSET *FormSet | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORMSET_STORAGE *FormSetStorage; | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
if (FormSetStorage->ConfigAltResp != NULL) { | |
FreePool (FormSetStorage->ConfigAltResp); | |
FormSetStorage->ConfigAltResp = NULL; | |
} | |
FormSetStorage->HasCallAltCfg = FALSE; | |
} | |
} | |
/** | |
Reset Questions to their initial value or default value in a Form, Formset or System. | |
GetDefaultValueScope parameter decides which questions will reset | |
to its default value. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param DefaultId The Class of the default. | |
@param SettingScope Setting Scope for Default action. | |
@param GetDefaultValueScope Get default value scope. | |
@param Storage Get default value only for this storage. | |
@param RetrieveValueFirst Whether call the retrieve call back to | |
get the initial value before get default | |
value. | |
@param SkipGetAltCfg Whether skip the get altcfg string process. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_UNSUPPORTED Unsupport SettingScope. | |
**/ | |
EFI_STATUS | |
ExtractDefault ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN UINT16 DefaultId, | |
IN BROWSER_SETTING_SCOPE SettingScope, | |
IN BROWSER_GET_DEFAULT_VALUE GetDefaultValueScope, | |
IN BROWSER_STORAGE *Storage OPTIONAL, | |
IN BOOLEAN RetrieveValueFirst, | |
IN BOOLEAN SkipGetAltCfg | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *FormLink; | |
LIST_ENTRY *Link; | |
FORM_BROWSER_STATEMENT *Question; | |
FORM_BROWSER_FORMSET *LocalFormSet; | |
FORM_BROWSER_FORMSET *OldFormSet; | |
Status = EFI_SUCCESS; | |
// | |
// Check the supported setting level. | |
// | |
if (SettingScope >= MaxLevel || GetDefaultValueScope >= GetDefaultForMax) { | |
return EFI_UNSUPPORTED; | |
} | |
if (GetDefaultValueScope == GetDefaultForStorage && Storage == NULL) { | |
return EFI_UNSUPPORTED; | |
} | |
if (SettingScope == FormLevel) { | |
// | |
// Prepare the AltCfg String for form. | |
// | |
if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { | |
ExtractAltCfgForForm (FormSet, Form, DefaultId, Storage); | |
} | |
// | |
// Extract Form default | |
// | |
Link = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, Link)) { | |
Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); | |
Link = GetNextNode (&Form->StatementListHead, Link); | |
// | |
// If get default value only for this storage, check the storage first. | |
// | |
if ((GetDefaultValueScope == GetDefaultForStorage) && (Question->Storage != Storage)) { | |
continue; | |
} | |
// | |
// If get default value only for no storage question, just skip the question which has storage. | |
// | |
if ((GetDefaultValueScope == GetDefaultForNoStorage) && (Question->Storage != NULL)) { | |
continue; | |
} | |
// | |
// If Question is disabled, don't reset it to default | |
// | |
if (Question->Expression != NULL) { | |
if (EvaluateExpressionList(Question->Expression, TRUE, FormSet, Form) == ExpressDisable) { | |
continue; | |
} | |
} | |
if (RetrieveValueFirst) { | |
// | |
// Call the Retrieve call back to get the initial question value. | |
// | |
Status = ProcessRetrieveForQuestion(FormSet->ConfigAccess, Question, FormSet); | |
} | |
// | |
// If not request to get the initial value or get initial value fail, then get default value. | |
// | |
if (!RetrieveValueFirst || EFI_ERROR (Status)) { | |
Status = GetQuestionDefault (FormSet, Form, Question, DefaultId); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
} | |
// | |
// Synchronize Buffer storage's Edit buffer | |
// | |
if ((Question->Storage != NULL) && | |
(Question->Storage->Type != EFI_HII_VARSTORE_EFI_VARIABLE)) { | |
SetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); | |
} | |
} | |
// | |
// Clean the AltCfg String. | |
// | |
if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { | |
CleanAltCfgForForm(Form); | |
} | |
} else if (SettingScope == FormSetLevel) { | |
// | |
// Prepare the AltCfg String for formset. | |
// | |
if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { | |
ExtractAltCfgForFormSet (FormSet, DefaultId, Storage); | |
} | |
FormLink = GetFirstNode (&FormSet->FormListHead); | |
while (!IsNull (&FormSet->FormListHead, FormLink)) { | |
Form = FORM_BROWSER_FORM_FROM_LINK (FormLink); | |
ExtractDefault (FormSet, Form, DefaultId, FormLevel, GetDefaultValueScope, Storage, RetrieveValueFirst, SkipGetAltCfg); | |
FormLink = GetNextNode (&FormSet->FormListHead, FormLink); | |
} | |
// | |
// Clean the AltCfg String. | |
// | |
if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { | |
CleanAltCfgForFormSet (FormSet); | |
} | |
} else if (SettingScope == SystemLevel) { | |
// | |
// Preload all Hii formset. | |
// | |
LoadAllHiiFormset(); | |
OldFormSet = mSystemLevelFormSet; | |
// | |
// Set Default Value for each FormSet in the maintain list. | |
// | |
Link = GetFirstNode (&gBrowserFormSetList); | |
while (!IsNull (&gBrowserFormSetList, Link)) { | |
LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); | |
Link = GetNextNode (&gBrowserFormSetList, Link); | |
if (!ValidateFormSet(LocalFormSet)) { | |
continue; | |
} | |
mSystemLevelFormSet = LocalFormSet; | |
ExtractDefault (LocalFormSet, NULL, DefaultId, FormSetLevel, GetDefaultValueScope, Storage, RetrieveValueFirst, SkipGetAltCfg); | |
} | |
mSystemLevelFormSet = OldFormSet; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Validate whether this question's value has changed. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@param Question Question to be initialized. | |
@param GetValueFrom Where to get value, may from editbuffer, buffer or hii driver. | |
@retval TRUE Question's value has changed. | |
@retval FALSE Question's value has not changed | |
**/ | |
BOOLEAN | |
IsQuestionValueChanged ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form, | |
IN OUT FORM_BROWSER_STATEMENT *Question, | |
IN GET_SET_QUESTION_VALUE_WITH GetValueFrom | |
) | |
{ | |
EFI_HII_VALUE BackUpValue; | |
CHAR8 *BackUpBuffer; | |
EFI_HII_VALUE BackUpValue2; | |
CHAR8 *BackUpBuffer2; | |
EFI_STATUS Status; | |
BOOLEAN ValueChanged; | |
UINTN BufferWidth; | |
// | |
// For quetion without storage, always mark it as data not changed. | |
// | |
if (Question->Storage == NULL && Question->Operand != EFI_IFR_TIME_OP && Question->Operand != EFI_IFR_DATE_OP) { | |
return FALSE; | |
} | |
BackUpBuffer = NULL; | |
BackUpBuffer2 = NULL; | |
ValueChanged = FALSE; | |
switch (Question->Operand) { | |
case EFI_IFR_ORDERED_LIST_OP: | |
BufferWidth = Question->StorageWidth; | |
BackUpBuffer = AllocateCopyPool (BufferWidth, Question->BufferValue); | |
ASSERT (BackUpBuffer != NULL); | |
break; | |
case EFI_IFR_STRING_OP: | |
case EFI_IFR_PASSWORD_OP: | |
BufferWidth = (UINTN) Question->Maximum * sizeof (CHAR16); | |
BackUpBuffer = AllocateCopyPool (BufferWidth, Question->BufferValue); | |
ASSERT (BackUpBuffer != NULL); | |
break; | |
default: | |
BufferWidth = 0; | |
break; | |
} | |
CopyMem (&BackUpValue, &Question->HiiValue, sizeof (EFI_HII_VALUE)); | |
if (GetValueFrom == GetSetValueWithBothBuffer) { | |
Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); | |
ASSERT_EFI_ERROR(Status); | |
switch (Question->Operand) { | |
case EFI_IFR_ORDERED_LIST_OP: | |
BufferWidth = Question->StorageWidth; | |
BackUpBuffer2 = AllocateCopyPool (BufferWidth, Question->BufferValue); | |
ASSERT (BackUpBuffer2 != NULL); | |
break; | |
case EFI_IFR_STRING_OP: | |
case EFI_IFR_PASSWORD_OP: | |
BufferWidth = (UINTN) Question->Maximum * sizeof (CHAR16); | |
BackUpBuffer2 = AllocateCopyPool (BufferWidth, Question->BufferValue); | |
ASSERT (BackUpBuffer2 != NULL); | |
break; | |
default: | |
BufferWidth = 0; | |
break; | |
} | |
CopyMem (&BackUpValue2, &Question->HiiValue, sizeof (EFI_HII_VALUE)); | |
Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithBuffer); | |
ASSERT_EFI_ERROR(Status); | |
if (CompareMem (&BackUpValue2, &Question->HiiValue, sizeof (EFI_HII_VALUE)) != 0 || | |
CompareMem (BackUpBuffer2, Question->BufferValue, BufferWidth) != 0) { | |
ValueChanged = TRUE; | |
} | |
} else { | |
Status = GetQuestionValue (FormSet, Form, Question, GetValueFrom); | |
ASSERT_EFI_ERROR(Status); | |
if (CompareMem (&BackUpValue, &Question->HiiValue, sizeof (EFI_HII_VALUE)) != 0 || | |
CompareMem (BackUpBuffer, Question->BufferValue, BufferWidth) != 0) { | |
ValueChanged = TRUE; | |
} | |
} | |
CopyMem (&Question->HiiValue, &BackUpValue, sizeof (EFI_HII_VALUE)); | |
if (BackUpBuffer != NULL) { | |
CopyMem (Question->BufferValue, BackUpBuffer, BufferWidth); | |
FreePool (BackUpBuffer); | |
} | |
if (BackUpBuffer2 != NULL) { | |
FreePool (BackUpBuffer2); | |
} | |
Question->ValueChanged = ValueChanged; | |
return ValueChanged; | |
} | |
/** | |
Initialize Question's Edit copy from Storage. | |
@param Selection Selection contains the information about | |
the Selection, form and formset to be displayed. | |
Selection action may be updated in retrieve callback. | |
If Selection is NULL, only initialize Question value. | |
@param FormSet FormSet data structure. | |
@param Form Form data structure. | |
@retval EFI_SUCCESS The function completed successfully. | |
**/ | |
EFI_STATUS | |
LoadFormConfig ( | |
IN OUT UI_MENU_SELECTION *Selection, | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORM_BROWSER_FORM *Form | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
FORM_BROWSER_STATEMENT *Question; | |
Link = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, Link)) { | |
Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); | |
// | |
// Initialize local copy of Value for each Question | |
// | |
if (Question->Operand == EFI_IFR_PASSWORD_OP && (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK)== 0) { | |
Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithHiiDriver); | |
} else { | |
Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if ((Question->Operand == EFI_IFR_STRING_OP) || (Question->Operand == EFI_IFR_PASSWORD_OP)) { | |
HiiSetString (FormSet->HiiHandle, Question->HiiValue.Value.string, (CHAR16*)Question->BufferValue, NULL); | |
} | |
Link = GetNextNode (&Form->StatementListHead, Link); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Initialize Question's Edit copy from Storage for the whole Formset. | |
@param Selection Selection contains the information about | |
the Selection, form and formset to be displayed. | |
Selection action may be updated in retrieve callback. | |
If Selection is NULL, only initialize Question value. | |
@param FormSet FormSet data structure. | |
@retval EFI_SUCCESS The function completed successfully. | |
**/ | |
EFI_STATUS | |
LoadFormSetConfig ( | |
IN OUT UI_MENU_SELECTION *Selection, | |
IN FORM_BROWSER_FORMSET *FormSet | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
FORM_BROWSER_FORM *Form; | |
Link = GetFirstNode (&FormSet->FormListHead); | |
while (!IsNull (&FormSet->FormListHead, Link)) { | |
Form = FORM_BROWSER_FORM_FROM_LINK (Link); | |
// | |
// Initialize local copy of Value for each Form | |
// | |
Status = LoadFormConfig (Selection, FormSet, Form); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Link = GetNextNode (&FormSet->FormListHead, Link); | |
} | |
// | |
// Finished question initialization. | |
// | |
FormSet->QuestionInited = TRUE; | |
return EFI_SUCCESS; | |
} | |
/** | |
Remove the Request element from the Config Request. | |
@param Storage Pointer to the browser storage. | |
@param RequestElement The pointer to the Request element. | |
**/ | |
VOID | |
RemoveElement ( | |
IN OUT BROWSER_STORAGE *Storage, | |
IN CHAR16 *RequestElement | |
) | |
{ | |
CHAR16 *NewStr; | |
CHAR16 *DestStr; | |
ASSERT (Storage->ConfigRequest != NULL && RequestElement != NULL); | |
NewStr = StrStr (Storage->ConfigRequest, RequestElement); | |
if (NewStr == NULL) { | |
return; | |
} | |
// | |
// Remove this element from this ConfigRequest. | |
// | |
DestStr = NewStr; | |
NewStr += StrLen (RequestElement); | |
CopyMem (DestStr, NewStr, StrSize (NewStr)); | |
Storage->SpareStrLen += StrLen (RequestElement); | |
} | |
/** | |
Adjust config request in storage, remove the request elements existed in the input ConfigRequest. | |
@param Storage Pointer to the formset storage. | |
@param ConfigRequest The pointer to the Request element. | |
**/ | |
VOID | |
RemoveConfigRequest ( | |
FORMSET_STORAGE *Storage, | |
CHAR16 *ConfigRequest | |
) | |
{ | |
CHAR16 *RequestElement; | |
CHAR16 *NextRequestElement; | |
CHAR16 *SearchKey; | |
// | |
// No request element in it, just return. | |
// | |
if (ConfigRequest == NULL) { | |
return; | |
} | |
if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
// | |
// "&Name1&Name2" section for EFI_HII_VARSTORE_NAME_VALUE storage | |
// | |
SearchKey = L"&"; | |
} else { | |
// | |
// "&OFFSET=####&WIDTH=####" section for EFI_HII_VARSTORE_BUFFER storage | |
// | |
SearchKey = L"&OFFSET"; | |
} | |
// | |
// Find SearchKey storage | |
// | |
if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
RequestElement = StrStr (ConfigRequest, L"PATH"); | |
ASSERT (RequestElement != NULL); | |
RequestElement = StrStr (RequestElement, SearchKey); | |
} else { | |
RequestElement = StrStr (ConfigRequest, SearchKey); | |
} | |
while (RequestElement != NULL) { | |
// | |
// +1 to avoid find header itself. | |
// | |
NextRequestElement = StrStr (RequestElement + 1, SearchKey); | |
// | |
// The last Request element in configRequest string. | |
// | |
if (NextRequestElement != NULL) { | |
// | |
// Replace "&" with '\0'. | |
// | |
*NextRequestElement = L'\0'; | |
} | |
RemoveElement (Storage->BrowserStorage, RequestElement); | |
if (NextRequestElement != NULL) { | |
// | |
// Restore '&' with '\0' for later used. | |
// | |
*NextRequestElement = L'&'; | |
} | |
RequestElement = NextRequestElement; | |
} | |
// | |
// If no request element remain, just remove the ConfigRequest string. | |
// | |
if (StrCmp (Storage->BrowserStorage->ConfigRequest, Storage->ConfigHdr) == 0) { | |
FreePool (Storage->BrowserStorage->ConfigRequest); | |
Storage->BrowserStorage->ConfigRequest = NULL; | |
Storage->BrowserStorage->SpareStrLen = 0; | |
} | |
} | |
/** | |
Base on the current formset info, clean the ConfigRequest string in browser storage. | |
@param FormSet Pointer of the FormSet | |
**/ | |
VOID | |
CleanBrowserStorage ( | |
IN OUT FORM_BROWSER_FORMSET *FormSet | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORMSET_STORAGE *Storage; | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
Storage = FORMSET_STORAGE_FROM_LINK (Link); | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { | |
if (Storage->ConfigRequest == NULL || Storage->BrowserStorage->ConfigRequest == NULL) { | |
continue; | |
} | |
RemoveConfigRequest (Storage, Storage->ConfigRequest); | |
} else if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_BUFFER || | |
Storage->BrowserStorage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
if (Storage->BrowserStorage->ConfigRequest != NULL) { | |
FreePool (Storage->BrowserStorage->ConfigRequest); | |
Storage->BrowserStorage->ConfigRequest = NULL; | |
} | |
Storage->BrowserStorage->Initialized = FALSE; | |
} | |
} | |
} | |
/** | |
Check whether current element in the ConfigReqeust string. | |
@param BrowserStorage Storage which includes ConfigReqeust. | |
@param RequestElement New element need to check. | |
@retval TRUE The Element is in the ConfigReqeust string. | |
@retval FALSE The Element not in the configReqeust String. | |
**/ | |
BOOLEAN | |
ElementValidation ( | |
BROWSER_STORAGE *BrowserStorage, | |
CHAR16 *RequestElement | |
) | |
{ | |
return StrStr (BrowserStorage->ConfigRequest, RequestElement) != NULL ? TRUE : FALSE; | |
} | |
/** | |
Append the Request element to the Config Request. | |
@param ConfigRequest Current ConfigRequest info. | |
@param SpareStrLen Current remain free buffer for config reqeust. | |
@param RequestElement New Request element. | |
**/ | |
VOID | |
AppendConfigRequest ( | |
IN OUT CHAR16 **ConfigRequest, | |
IN OUT UINTN *SpareStrLen, | |
IN CHAR16 *RequestElement | |
) | |
{ | |
CHAR16 *NewStr; | |
UINTN StringSize; | |
UINTN StrLength; | |
StrLength = StrLen (RequestElement); | |
// | |
// Append <RequestElement> to <ConfigRequest> | |
// | |
if (StrLength > *SpareStrLen) { | |
// | |
// Old String buffer is not sufficient for RequestElement, allocate a new one | |
// | |
StringSize = (*ConfigRequest != NULL) ? StrSize (*ConfigRequest) : sizeof (CHAR16); | |
NewStr = AllocateZeroPool (StringSize + CONFIG_REQUEST_STRING_INCREMENTAL * sizeof (CHAR16)); | |
ASSERT (NewStr != NULL); | |
if (*ConfigRequest != NULL) { | |
CopyMem (NewStr, *ConfigRequest, StringSize); | |
FreePool (*ConfigRequest); | |
} | |
*ConfigRequest = NewStr; | |
*SpareStrLen = CONFIG_REQUEST_STRING_INCREMENTAL; | |
} | |
StrCat (*ConfigRequest, RequestElement); | |
*SpareStrLen -= StrLength; | |
} | |
/** | |
Adjust the config request info, remove the request elements which already in AllConfigRequest string. | |
@param Storage Form set Storage. | |
@param Request The input request string. | |
@param RespString Whether the input is ConfigRequest or ConfigResp format. | |
@retval TRUE Has element not covered by current used elements, need to continue to call ExtractConfig | |
@retval FALSE All elements covered by current used elements. | |
**/ | |
BOOLEAN | |
ConfigRequestAdjust ( | |
IN BROWSER_STORAGE *Storage, | |
IN CHAR16 *Request, | |
IN BOOLEAN RespString | |
) | |
{ | |
CHAR16 *RequestElement; | |
CHAR16 *NextRequestElement; | |
CHAR16 *NextElementBakup; | |
CHAR16 *SearchKey; | |
CHAR16 *ValueKey; | |
BOOLEAN RetVal; | |
CHAR16 *ConfigRequest; | |
RetVal = FALSE; | |
NextElementBakup = NULL; | |
ValueKey = NULL; | |
if (Request != NULL) { | |
ConfigRequest = Request; | |
} else { | |
ConfigRequest = Storage->ConfigRequest; | |
} | |
if (Storage->ConfigRequest == NULL) { | |
Storage->ConfigRequest = AllocateCopyPool (StrSize (ConfigRequest), ConfigRequest); | |
return TRUE; | |
} | |
if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
// | |
// "&Name1&Name2" section for EFI_HII_VARSTORE_NAME_VALUE storage | |
// | |
SearchKey = L"&"; | |
} else { | |
// | |
// "&OFFSET=####&WIDTH=####" section for EFI_HII_VARSTORE_BUFFER storage | |
// | |
SearchKey = L"&OFFSET"; | |
ValueKey = L"&VALUE"; | |
} | |
// | |
// Find SearchKey storage | |
// | |
if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { | |
RequestElement = StrStr (ConfigRequest, L"PATH"); | |
ASSERT (RequestElement != NULL); | |
RequestElement = StrStr (RequestElement, SearchKey); | |
} else { | |
RequestElement = StrStr (ConfigRequest, SearchKey); | |
} | |
while (RequestElement != NULL) { | |
// | |
// +1 to avoid find header itself. | |
// | |
NextRequestElement = StrStr (RequestElement + 1, SearchKey); | |
// | |
// The last Request element in configRequest string. | |
// | |
if (NextRequestElement != NULL) { | |
if (RespString && (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { | |
NextElementBakup = NextRequestElement; | |
NextRequestElement = StrStr (RequestElement, ValueKey); | |
ASSERT (NextRequestElement != NULL); | |
} | |
// | |
// Replace "&" with '\0'. | |
// | |
*NextRequestElement = L'\0'; | |
} else { | |
if (RespString && (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { | |
NextElementBakup = NextRequestElement; | |
NextRequestElement = StrStr (RequestElement, ValueKey); | |
ASSERT (NextRequestElement != NULL); | |
// | |
// Replace "&" with '\0'. | |
// | |
*NextRequestElement = L'\0'; | |
} | |
} | |
if (!ElementValidation (Storage, RequestElement)) { | |
// | |
// Add this element to the Storage->BrowserStorage->AllRequestElement. | |
// | |
AppendConfigRequest(&Storage->ConfigRequest, &Storage->SpareStrLen, RequestElement); | |
RetVal = TRUE; | |
} | |
if (NextRequestElement != NULL) { | |
// | |
// Restore '&' with '\0' for later used. | |
// | |
*NextRequestElement = L'&'; | |
} | |
if (RespString && (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { | |
RequestElement = NextElementBakup; | |
} else { | |
RequestElement = NextRequestElement; | |
} | |
} | |
return RetVal; | |
} | |
/** | |
Fill storage's edit copy with settings requested from Configuration Driver. | |
@param FormSet FormSet data structure. | |
@param Storage Buffer Storage. | |
**/ | |
VOID | |
LoadStorage ( | |
IN FORM_BROWSER_FORMSET *FormSet, | |
IN FORMSET_STORAGE *Storage | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_STRING Progress; | |
EFI_STRING Result; | |
CHAR16 *StrPtr; | |
EFI_STRING ConfigRequest; | |
UINTN StrLen; | |
ConfigRequest = NULL; | |
switch (Storage->BrowserStorage->Type) { | |
case EFI_HII_VARSTORE_EFI_VARIABLE: | |
return; | |
case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: | |
if (Storage->BrowserStorage->ConfigRequest != NULL) { | |
ConfigRequestAdjust(Storage->BrowserStorage, Storage->ConfigRequest, FALSE); | |
return; | |
} | |
break; | |
case EFI_HII_VARSTORE_BUFFER: | |
case EFI_HII_VARSTORE_NAME_VALUE: | |
// | |
// Skip if there is no RequestElement. | |
// | |
if (Storage->ElementCount == 0) { | |
return; | |
} | |
// | |
// Just update the ConfigRequest, if storage already initialized. | |
// | |
if (Storage->BrowserStorage->Initialized) { | |
ConfigRequestAdjust(Storage->BrowserStorage, Storage->ConfigRequest, FALSE); | |
return; | |
} | |
Storage->BrowserStorage->Initialized = TRUE; | |
break; | |
default: | |
return; | |
} | |
if (Storage->BrowserStorage->Type != EFI_HII_VARSTORE_NAME_VALUE) { | |
// | |
// Create the config request string to get all fields for this storage. | |
// Allocate and fill a buffer large enough to hold the <ConfigHdr> template | |
// followed by "&OFFSET=0&WIDTH=WWWW"followed by a Null-terminator | |
// | |
StrLen = StrSize (Storage->ConfigHdr) + 20 * sizeof (CHAR16); | |
ConfigRequest = AllocateZeroPool (StrLen); | |
ASSERT (ConfigRequest != NULL); | |
UnicodeSPrint ( | |
ConfigRequest, | |
StrLen, | |
L"%s&OFFSET=0&WIDTH=%04x", | |
Storage->ConfigHdr, | |
Storage->BrowserStorage->Size); | |
} else { | |
ConfigRequest = Storage->ConfigRequest; | |
} | |
// | |
// Request current settings from Configuration Driver | |
// | |
Status = mHiiConfigRouting->ExtractConfig ( | |
mHiiConfigRouting, | |
ConfigRequest, | |
&Progress, | |
&Result | |
); | |
// | |
// If get value fail, extract default from IFR binary | |
// | |
if (EFI_ERROR (Status)) { | |
ExtractDefault (FormSet, NULL, EFI_HII_DEFAULT_CLASS_STANDARD, FormSetLevel, GetDefaultForStorage, Storage->BrowserStorage, TRUE, TRUE); | |
} else { | |
// | |
// Convert Result from <ConfigAltResp> to <ConfigResp> | |
// | |
StrPtr = StrStr (Result, L"&GUID="); | |
if (StrPtr != NULL) { | |
*StrPtr = L'\0'; | |
} | |
Status = ConfigRespToStorage (Storage->BrowserStorage, Result); | |
FreePool (Result); | |
} | |
Storage->BrowserStorage->ConfigRequest = AllocateCopyPool (StrSize (Storage->ConfigRequest), Storage->ConfigRequest); | |
// | |
// Input NULL for ConfigRequest field means sync all fields from editbuffer to buffer. | |
// | |
SynchronizeStorage(Storage->BrowserStorage, NULL, TRUE); | |
if (Storage->BrowserStorage->Type != EFI_HII_VARSTORE_NAME_VALUE) { | |
if (ConfigRequest != NULL) { | |
FreePool (ConfigRequest); | |
} | |
} | |
} | |
/** | |
Get Value changed status from old question. | |
@param NewFormSet FormSet data structure. | |
@param OldQuestion Old question which has value changed. | |
**/ | |
VOID | |
SyncStatusForQuestion ( | |
IN OUT FORM_BROWSER_FORMSET *NewFormSet, | |
IN FORM_BROWSER_STATEMENT *OldQuestion | |
) | |
{ | |
LIST_ENTRY *Link; | |
LIST_ENTRY *QuestionLink; | |
FORM_BROWSER_FORM *Form; | |
FORM_BROWSER_STATEMENT *Question; | |
// | |
// For each form in one formset. | |
// | |
Link = GetFirstNode (&NewFormSet->FormListHead); | |
while (!IsNull (&NewFormSet->FormListHead, Link)) { | |
Form = FORM_BROWSER_FORM_FROM_LINK (Link); | |
Link = GetNextNode (&NewFormSet->FormListHead, Link); | |
// | |
// for each question in one form. | |
// | |
QuestionLink = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, QuestionLink)) { | |
Question = FORM_BROWSER_STATEMENT_FROM_LINK (QuestionLink); | |
QuestionLink = GetNextNode (&Form->StatementListHead, QuestionLink); | |
if (Question->QuestionId == OldQuestion->QuestionId) { | |
Question->ValueChanged = TRUE; | |
return; | |
} | |
} | |
} | |
} | |
/** | |
Get Value changed status from old formset. | |
@param NewFormSet FormSet data structure. | |
@param OldFormSet FormSet data structure. | |
**/ | |
VOID | |
SyncStatusForFormSet ( | |
IN OUT FORM_BROWSER_FORMSET *NewFormSet, | |
IN FORM_BROWSER_FORMSET *OldFormSet | |
) | |
{ | |
LIST_ENTRY *Link; | |
LIST_ENTRY *QuestionLink; | |
FORM_BROWSER_FORM *Form; | |
FORM_BROWSER_STATEMENT *Question; | |
// | |
// For each form in one formset. | |
// | |
Link = GetFirstNode (&OldFormSet->FormListHead); | |
while (!IsNull (&OldFormSet->FormListHead, Link)) { | |
Form = FORM_BROWSER_FORM_FROM_LINK (Link); | |
Link = GetNextNode (&OldFormSet->FormListHead, Link); | |
// | |
// for each question in one form. | |
// | |
QuestionLink = GetFirstNode (&Form->StatementListHead); | |
while (!IsNull (&Form->StatementListHead, QuestionLink)) { | |
Question = FORM_BROWSER_STATEMENT_FROM_LINK (QuestionLink); | |
QuestionLink = GetNextNode (&Form->StatementListHead, QuestionLink); | |
if (!Question->ValueChanged) { | |
continue; | |
} | |
// | |
// Find the same question in new formset and update the value changed flag. | |
// | |
SyncStatusForQuestion (NewFormSet, Question); | |
} | |
} | |
} | |
/** | |
Get current setting of Questions. | |
@param FormSet FormSet data structure. | |
**/ | |
VOID | |
InitializeCurrentSetting ( | |
IN OUT FORM_BROWSER_FORMSET *FormSet | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORMSET_STORAGE *Storage; | |
FORM_BROWSER_FORMSET *OldFormSet; | |
// | |
// Try to find pre FormSet in the maintain backup list. | |
// If old formset != NULL, destroy this formset. Add new formset to gBrowserFormSetList. | |
// | |
OldFormSet = GetFormSetFromHiiHandle (FormSet->HiiHandle); | |
if (OldFormSet != NULL) { | |
SyncStatusForFormSet (FormSet, OldFormSet); | |
RemoveEntryList (&OldFormSet->Link); | |
DestroyFormSet (OldFormSet); | |
} | |
InsertTailList (&gBrowserFormSetList, &FormSet->Link); | |
// | |
// Extract default from IFR binary for no storage questions. | |
// | |
ExtractDefault (FormSet, NULL, EFI_HII_DEFAULT_CLASS_STANDARD, FormSetLevel, GetDefaultForNoStorage, NULL, TRUE, FALSE); | |
// | |
// Request current settings from Configuration Driver | |
// | |
Link = GetFirstNode (&FormSet->StorageListHead); | |
while (!IsNull (&FormSet->StorageListHead, Link)) { | |
Storage = FORMSET_STORAGE_FROM_LINK (Link); | |
LoadStorage (FormSet, Storage); | |
Link = GetNextNode (&FormSet->StorageListHead, Link); | |
} | |
} | |
/** | |
Fetch the Ifr binary data of a FormSet. | |
@param Handle PackageList Handle | |
@param FormSetGuid On input, GUID or class GUID of a formset. If not | |
specified (NULL or zero GUID), take the first | |
FormSet with class GUID EFI_HII_PLATFORM_SETUP_FORMSET_GUID | |
found in package list. | |
On output, GUID of the formset found(if not NULL). | |
@param BinaryLength The length of the FormSet IFR binary. | |
@param BinaryData The buffer designed to receive the FormSet. | |
@retval EFI_SUCCESS Buffer filled with the requested FormSet. | |
BufferLength was updated. | |
@retval EFI_INVALID_PARAMETER The handle is unknown. | |
@retval EFI_NOT_FOUND A form or FormSet on the requested handle cannot | |
be found with the requested FormId. | |
**/ | |
EFI_STATUS | |
GetIfrBinaryData ( | |
IN EFI_HII_HANDLE Handle, | |
IN OUT EFI_GUID *FormSetGuid, | |
OUT UINTN *BinaryLength, | |
OUT UINT8 **BinaryData | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HII_PACKAGE_LIST_HEADER *HiiPackageList; | |
UINTN BufferSize; | |
UINT8 *Package; | |
UINT8 *OpCodeData; | |
UINT32 Offset; | |
UINT32 Offset2; | |
UINT32 PackageListLength; | |
EFI_HII_PACKAGE_HEADER PackageHeader; | |
UINT8 Index; | |
UINT8 NumberOfClassGuid; | |
BOOLEAN ClassGuidMatch; | |
EFI_GUID *ClassGuid; | |
EFI_GUID *ComparingGuid; | |
OpCodeData = NULL; | |
Package = NULL; | |
ZeroMem (&PackageHeader, sizeof (EFI_HII_PACKAGE_HEADER)); | |
// | |
// if FormSetGuid is NULL or zero GUID, return first Setup FormSet in the package list | |
// | |
if (FormSetGuid == NULL) { | |
ComparingGuid = &gZeroGuid; | |
} else { | |
ComparingGuid = FormSetGuid; | |
} | |
// | |
// Get HII PackageList | |
// | |
BufferSize = 0; | |
HiiPackageList = NULL; | |
Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, Handle, &BufferSize, HiiPackageList); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
HiiPackageList = AllocatePool (BufferSize); | |
ASSERT (HiiPackageList != NULL); | |
Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, Handle, &BufferSize, HiiPackageList); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ASSERT (HiiPackageList != NULL); | |
// | |
// Get Form package from this HII package List | |
// | |
Offset = sizeof (EFI_HII_PACKAGE_LIST_HEADER); | |
Offset2 = 0; | |
CopyMem (&PackageListLength, &HiiPackageList->PackageLength, sizeof (UINT32)); | |
ClassGuidMatch = FALSE; | |
while (Offset < PackageListLength) { | |
Package = ((UINT8 *) HiiPackageList) + Offset; | |
CopyMem (&PackageHeader, Package, sizeof (EFI_HII_PACKAGE_HEADER)); | |
if (PackageHeader.Type == EFI_HII_PACKAGE_FORMS) { | |
// | |
// Search FormSet in this Form Package | |
// | |
Offset2 = sizeof (EFI_HII_PACKAGE_HEADER); | |
while (Offset2 < PackageHeader.Length) { | |
OpCodeData = Package + Offset2; | |
if (((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode == EFI_IFR_FORM_SET_OP) { | |
// | |
// Try to compare against formset GUID | |
// | |
if (CompareGuid (FormSetGuid, &gZeroGuid) || | |
CompareGuid (ComparingGuid, (EFI_GUID *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) { | |
break; | |
} | |
if (((EFI_IFR_OP_HEADER *) OpCodeData)->Length > OFFSET_OF (EFI_IFR_FORM_SET, Flags)) { | |
// | |
// Try to compare against formset class GUID | |
// | |
NumberOfClassGuid = (UINT8) (((EFI_IFR_FORM_SET *) OpCodeData)->Flags & 0x3); | |
ClassGuid = (EFI_GUID *) (OpCodeData + sizeof (EFI_IFR_FORM_SET)); | |
for (Index = 0; Index < NumberOfClassGuid; Index++) { | |
if (CompareGuid (ComparingGuid, ClassGuid + Index)) { | |
ClassGuidMatch = TRUE; | |
break; | |
} | |
} | |
if (ClassGuidMatch) { | |
break; | |
} | |
} else if (ComparingGuid == &gEfiHiiPlatformSetupFormsetGuid) { | |
ClassGuidMatch = TRUE; | |
break; | |
} | |
} | |
Offset2 += ((EFI_IFR_OP_HEADER *) OpCodeData)->Length; | |
} | |
if (Offset2 < PackageHeader.Length) { | |
// | |
// Target formset found | |
// | |
break; | |
} | |
} | |
Offset += PackageHeader.Length; | |
} | |
if (Offset >= PackageListLength) { | |
// | |
// Form package not found in this Package List | |
// | |
FreePool (HiiPackageList); | |
return EFI_NOT_FOUND; | |
} | |
if (FormSetGuid != NULL) { | |
// | |
// Return the FormSet GUID | |
// | |
CopyMem (FormSetGuid, &((EFI_IFR_FORM_SET *) OpCodeData)->Guid, sizeof (EFI_GUID)); | |
} | |
// | |
// To determine the length of a whole FormSet IFR binary, one have to parse all the Opcodes | |
// in this FormSet; So, here just simply copy the data from start of a FormSet to the end | |
// of the Form Package. | |
// | |
*BinaryLength = PackageHeader.Length - Offset2; | |
*BinaryData = AllocateCopyPool (*BinaryLength, OpCodeData); | |
FreePool (HiiPackageList); | |
if (*BinaryData == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Initialize the internal data structure of a FormSet. | |
@param Handle PackageList Handle | |
@param FormSetGuid On input, GUID or class GUID of a formset. If not | |
specified (NULL or zero GUID), take the first | |
FormSet with class GUID EFI_HII_PLATFORM_SETUP_FORMSET_GUID | |
found in package list. | |
On output, GUID of the formset found(if not NULL). | |
@param FormSet FormSet data structure. | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_NOT_FOUND The specified FormSet could not be found. | |
**/ | |
EFI_STATUS | |
InitializeFormSet ( | |
IN EFI_HII_HANDLE Handle, | |
IN OUT EFI_GUID *FormSetGuid, | |
OUT FORM_BROWSER_FORMSET *FormSet | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE DriverHandle; | |
Status = GetIfrBinaryData (Handle, FormSetGuid, &FormSet->IfrBinaryLength, &FormSet->IfrBinaryData); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
FormSet->Signature = FORM_BROWSER_FORMSET_SIGNATURE; | |
FormSet->HiiHandle = Handle; | |
CopyMem (&FormSet->Guid, FormSetGuid, sizeof (EFI_GUID)); | |
FormSet->QuestionInited = FALSE; | |
// | |
// Retrieve ConfigAccess Protocol associated with this HiiPackageList | |
// | |
Status = mHiiDatabase->GetPackageListHandle (mHiiDatabase, Handle, &DriverHandle); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
FormSet->DriverHandle = DriverHandle; | |
Status = gBS->HandleProtocol ( | |
DriverHandle, | |
&gEfiHiiConfigAccessProtocolGuid, | |
(VOID **) &FormSet->ConfigAccess | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// Configuration Driver don't attach ConfigAccess protocol to its HII package | |
// list, then there will be no configuration action required | |
// | |
FormSet->ConfigAccess = NULL; | |
} | |
// | |
// Parse the IFR binary OpCodes | |
// | |
Status = ParseOpCodes (FormSet); | |
return Status; | |
} | |
/** | |
Save globals used by previous call to SendForm(). SendForm() may be called from | |
HiiConfigAccess.Callback(), this will cause SendForm() be reentried. | |
So, save globals of previous call to SendForm() and restore them upon exit. | |
**/ | |
VOID | |
SaveBrowserContext ( | |
VOID | |
) | |
{ | |
BROWSER_CONTEXT *Context; | |
FORM_ENTRY_INFO *MenuList; | |
gBrowserContextCount++; | |
if (gBrowserContextCount == 1) { | |
// | |
// This is not reentry of SendForm(), no context to save | |
// | |
return; | |
} | |
Context = AllocatePool (sizeof (BROWSER_CONTEXT)); | |
ASSERT (Context != NULL); | |
Context->Signature = BROWSER_CONTEXT_SIGNATURE; | |
// | |
// Save FormBrowser context | |
// | |
Context->Selection = gCurrentSelection; | |
Context->ResetRequired = gResetRequired; | |
Context->ExitRequired = gExitRequired; | |
Context->HiiHandle = mCurrentHiiHandle; | |
Context->FormId = mCurrentFormId; | |
CopyGuid (&Context->FormSetGuid, &mCurrentFormSetGuid); | |
// | |
// Save the menu history data. | |
// | |
InitializeListHead(&Context->FormHistoryList); | |
while (!IsListEmpty (&mPrivateData.FormBrowserEx2.FormViewHistoryHead)) { | |
MenuList = FORM_ENTRY_INFO_FROM_LINK (mPrivateData.FormBrowserEx2.FormViewHistoryHead.ForwardLink); | |
RemoveEntryList (&MenuList->Link); | |
InsertTailList(&Context->FormHistoryList, &MenuList->Link); | |
} | |
// | |
// Insert to FormBrowser context list | |
// | |
InsertHeadList (&gBrowserContextList, &Context->Link); | |
} | |
/** | |
Restore globals used by previous call to SendForm(). | |
**/ | |
VOID | |
RestoreBrowserContext ( | |
VOID | |
) | |
{ | |
LIST_ENTRY *Link; | |
BROWSER_CONTEXT *Context; | |
FORM_ENTRY_INFO *MenuList; | |
ASSERT (gBrowserContextCount != 0); | |
gBrowserContextCount--; | |
if (gBrowserContextCount == 0) { | |
// | |
// This is not reentry of SendForm(), no context to restore | |
// | |
return; | |
} | |
ASSERT (!IsListEmpty (&gBrowserContextList)); | |
Link = GetFirstNode (&gBrowserContextList); | |
Context = BROWSER_CONTEXT_FROM_LINK (Link); | |
// | |
// Restore FormBrowser context | |
// | |
gCurrentSelection = Context->Selection; | |
gResetRequired = Context->ResetRequired; | |
gExitRequired = Context->ExitRequired; | |
mCurrentHiiHandle = Context->HiiHandle; | |
mCurrentFormId = Context->FormId; | |
CopyGuid (&mCurrentFormSetGuid, &Context->FormSetGuid); | |
// | |
// Restore the menu history data. | |
// | |
while (!IsListEmpty (&Context->FormHistoryList)) { | |
MenuList = FORM_ENTRY_INFO_FROM_LINK (Context->FormHistoryList.ForwardLink); | |
RemoveEntryList (&MenuList->Link); | |
InsertTailList(&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &MenuList->Link); | |
} | |
// | |
// Remove from FormBrowser context list | |
// | |
RemoveEntryList (&Context->Link); | |
gBS->FreePool (Context); | |
} | |
/** | |
Find the matched FormSet context in the backup maintain list based on HiiHandle. | |
@param Handle The Hii Handle. | |
@return the found FormSet context. If no found, NULL will return. | |
**/ | |
FORM_BROWSER_FORMSET * | |
GetFormSetFromHiiHandle ( | |
EFI_HII_HANDLE Handle | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_BROWSER_FORMSET *FormSet; | |
Link = GetFirstNode (&gBrowserFormSetList); | |
while (!IsNull (&gBrowserFormSetList, Link)) { | |
FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); | |
Link = GetNextNode (&gBrowserFormSetList, Link); | |
if (!ValidateFormSet(FormSet)) { | |
continue; | |
} | |
if (FormSet->HiiHandle == Handle) { | |
return FormSet; | |
} | |
} | |
return NULL; | |
} | |
/** | |
Check whether the input HII handle is the FormSet that is being used. | |
@param Handle The Hii Handle. | |
@retval TRUE HII handle is being used. | |
@retval FALSE HII handle is not being used. | |
**/ | |
BOOLEAN | |
IsHiiHandleInBrowserContext ( | |
EFI_HII_HANDLE Handle | |
) | |
{ | |
LIST_ENTRY *Link; | |
BROWSER_CONTEXT *Context; | |
// | |
// HiiHandle is Current FormSet. | |
// | |
if (mCurrentHiiHandle == Handle) { | |
return TRUE; | |
} | |
// | |
// Check whether HiiHandle is in BrowserContext. | |
// | |
Link = GetFirstNode (&gBrowserContextList); | |
while (!IsNull (&gBrowserContextList, Link)) { | |
Context = BROWSER_CONTEXT_FROM_LINK (Link); | |
if (Context->HiiHandle == Handle) { | |
// | |
// HiiHandle is in BrowserContext | |
// | |
return TRUE; | |
} | |
Link = GetNextNode (&gBrowserContextList, Link); | |
} | |
return FALSE; | |
} | |
/** | |
Perform Password check. | |
Passwork may be encrypted by driver that requires the specific check. | |
@param Form Form where Password Statement is in. | |
@param Statement Password statement | |
@param PasswordString Password string to be checked. It may be NULL. | |
NULL means to restore password. | |
"" string can be used to checked whether old password does exist. | |
@return Status Status of Password check. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PasswordCheck ( | |
IN FORM_DISPLAY_ENGINE_FORM *Form, | |
IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, | |
IN EFI_STRING PasswordString OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; | |
EFI_BROWSER_ACTION_REQUEST ActionRequest; | |
EFI_IFR_TYPE_VALUE IfrTypeValue; | |
FORM_BROWSER_STATEMENT *Question; | |
ConfigAccess = gCurrentSelection->FormSet->ConfigAccess; | |
Question = GetBrowserStatement(Statement); | |
ASSERT (Question != NULL); | |
if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) == EFI_IFR_FLAG_CALLBACK) { | |
if (ConfigAccess == NULL) { | |
return EFI_UNSUPPORTED; | |
} | |
} else { | |
if (PasswordString == NULL) { | |
return EFI_SUCCESS; | |
} | |
// | |
// Check whether has preexisted password. | |
// | |
if (PasswordString[0] == 0) { | |
if (*((CHAR16 *) Question->BufferValue) == 0) { | |
return EFI_SUCCESS; | |
} else { | |
return EFI_NOT_READY; | |
} | |
} | |
// | |
// Check whether the input password is same as preexisted password. | |
// | |
if (StrnCmp (PasswordString, (CHAR16 *) Question->BufferValue, Question->StorageWidth/sizeof (CHAR16)) == 0) { | |
return EFI_SUCCESS; | |
} else { | |
return EFI_NOT_READY; | |
} | |
} | |
// | |
// Prepare password string in HII database | |
// | |
if (PasswordString != NULL) { | |
IfrTypeValue.string = NewString (PasswordString, gCurrentSelection->FormSet->HiiHandle); | |
} else { | |
IfrTypeValue.string = 0; | |
} | |
// | |
// Send password to Configuration Driver for validation | |
// | |
Status = ConfigAccess->Callback ( | |
ConfigAccess, | |
EFI_BROWSER_ACTION_CHANGING, | |
Question->QuestionId, | |
Question->HiiValue.Type, | |
&IfrTypeValue, | |
&ActionRequest | |
); | |
// | |
// Remove password string from HII database | |
// | |
if (PasswordString != NULL) { | |
DeleteString (IfrTypeValue.string, gCurrentSelection->FormSet->HiiHandle); | |
} | |
return Status; | |
} | |
/** | |
Find the registered HotKey based on KeyData. | |
@param[in] KeyData A pointer to a buffer that describes the keystroke | |
information for the hot key. | |
@return The registered HotKey context. If no found, NULL will return. | |
**/ | |
BROWSER_HOT_KEY * | |
GetHotKeyFromRegisterList ( | |
IN EFI_INPUT_KEY *KeyData | |
) | |
{ | |
LIST_ENTRY *Link; | |
BROWSER_HOT_KEY *HotKey; | |
Link = GetFirstNode (&gBrowserHotKeyList); | |
while (!IsNull (&gBrowserHotKeyList, Link)) { | |
HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); | |
if (HotKey->KeyData->ScanCode == KeyData->ScanCode) { | |
return HotKey; | |
} | |
Link = GetNextNode (&gBrowserHotKeyList, Link); | |
} | |
return NULL; | |
} | |
/** | |
Configure what scope the hot key will impact. | |
All hot keys have the same scope. The mixed hot keys with the different level are not supported. | |
If no scope is set, the default scope will be FormSet level. | |
After all registered hot keys are removed, previous Scope can reset to another level. | |
@param[in] Scope Scope level to be set. | |
@retval EFI_SUCCESS Scope is set correctly. | |
@retval EFI_INVALID_PARAMETER Scope is not the valid value specified in BROWSER_SETTING_SCOPE. | |
@retval EFI_UNSPPORTED Scope level is different from current one that the registered hot keys have. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SetScope ( | |
IN BROWSER_SETTING_SCOPE Scope | |
) | |
{ | |
if (Scope >= MaxLevel) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// When no hot key registered in system or on the first setting, | |
// Scope can be set. | |
// | |
if (mBrowserScopeFirstSet || IsListEmpty (&gBrowserHotKeyList)) { | |
gBrowserSettingScope = Scope; | |
mBrowserScopeFirstSet = FALSE; | |
} else if (Scope != gBrowserSettingScope) { | |
return EFI_UNSUPPORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Register the hot key with its browser action, or unregistered the hot key. | |
Only support hot key that is not printable character (control key, function key, etc.). | |
If the action value is zero, the hot key will be unregistered if it has been registered. | |
If the same hot key has been registered, the new action and help string will override the previous ones. | |
@param[in] KeyData A pointer to a buffer that describes the keystroke | |
information for the hot key. Its type is EFI_INPUT_KEY to | |
be supported by all ConsoleIn devices. | |
@param[in] Action Action value that describes what action will be trigged when the hot key is pressed. | |
@param[in] DefaultId Specifies the type of defaults to retrieve, which is only for DEFAULT action. | |
@param[in] HelpString Help string that describes the hot key information. | |
Its value may be NULL for the unregistered hot key. | |
@retval EFI_SUCCESS Hot key is registered or unregistered. | |
@retval EFI_INVALID_PARAMETER KeyData is NULL or HelpString is NULL on register. | |
@retval EFI_NOT_FOUND KeyData is not found to be unregistered. | |
@retval EFI_UNSUPPORTED Key represents a printable character. It is conflicted with Browser. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RegisterHotKey ( | |
IN EFI_INPUT_KEY *KeyData, | |
IN UINT32 Action, | |
IN UINT16 DefaultId, | |
IN EFI_STRING HelpString OPTIONAL | |
) | |
{ | |
BROWSER_HOT_KEY *HotKey; | |
// | |
// Check input parameters. | |
// | |
if (KeyData == NULL || KeyData->UnicodeChar != CHAR_NULL || | |
(Action != BROWSER_ACTION_UNREGISTER && HelpString == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check whether the input KeyData is in BrowserHotKeyList. | |
// | |
HotKey = GetHotKeyFromRegisterList (KeyData); | |
// | |
// Unregister HotKey | |
// | |
if (Action == BROWSER_ACTION_UNREGISTER) { | |
if (HotKey != NULL) { | |
// | |
// The registered HotKey is found. | |
// Remove it from List, and free its resource. | |
// | |
RemoveEntryList (&HotKey->Link); | |
FreePool (HotKey->KeyData); | |
FreePool (HotKey->HelpString); | |
return EFI_SUCCESS; | |
} else { | |
// | |
// The registered HotKey is not found. | |
// | |
return EFI_NOT_FOUND; | |
} | |
} | |
// | |
// Register HotKey into List. | |
// | |
if (HotKey == NULL) { | |
// | |
// Create new Key, and add it into List. | |
// | |
HotKey = AllocateZeroPool (sizeof (BROWSER_HOT_KEY)); | |
ASSERT (HotKey != NULL); | |
HotKey->Signature = BROWSER_HOT_KEY_SIGNATURE; | |
HotKey->KeyData = AllocateCopyPool (sizeof (EFI_INPUT_KEY), KeyData); | |
InsertTailList (&gBrowserHotKeyList, &HotKey->Link); | |
} | |
// | |
// Fill HotKey information. | |
// | |
HotKey->Action = Action; | |
HotKey->DefaultId = DefaultId; | |
if (HotKey->HelpString != NULL) { | |
FreePool (HotKey->HelpString); | |
} | |
HotKey->HelpString = AllocateCopyPool (StrSize (HelpString), HelpString); | |
return EFI_SUCCESS; | |
} | |
/** | |
Register Exit handler function. | |
When more than one handler function is registered, the latter one will override the previous one. | |
When NULL handler is specified, the previous Exit handler will be unregistered. | |
@param[in] Handler Pointer to handler function. | |
**/ | |
VOID | |
EFIAPI | |
RegiserExitHandler ( | |
IN EXIT_HANDLER Handler | |
) | |
{ | |
ExitHandlerFunction = Handler; | |
return; | |
} | |
/** | |
Check whether the browser data has been modified. | |
@retval TRUE Browser data is modified. | |
@retval FALSE No browser data is modified. | |
**/ | |
BOOLEAN | |
EFIAPI | |
IsBrowserDataModified ( | |
VOID | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_BROWSER_FORMSET *FormSet; | |
switch (gBrowserSettingScope) { | |
case FormLevel: | |
if (gCurrentSelection == NULL) { | |
return FALSE; | |
} | |
return IsNvUpdateRequiredForForm (gCurrentSelection->Form); | |
case FormSetLevel: | |
if (gCurrentSelection == NULL) { | |
return FALSE; | |
} | |
return IsNvUpdateRequiredForFormSet (gCurrentSelection->FormSet); | |
case SystemLevel: | |
Link = GetFirstNode (&gBrowserFormSetList); | |
while (!IsNull (&gBrowserFormSetList, Link)) { | |
FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); | |
if (!ValidateFormSet(FormSet)) { | |
continue; | |
} | |
if (IsNvUpdateRequiredForFormSet (FormSet)) { | |
return TRUE; | |
} | |
Link = GetNextNode (&gBrowserFormSetList, Link); | |
} | |
return FALSE; | |
default: | |
return FALSE; | |
} | |
} | |
/** | |
Execute the action requested by the Action parameter. | |
@param[in] Action Execute the request action. | |
@param[in] DefaultId The default Id info when need to load default value. Only used when Action is BROWSER_ACTION_DEFAULT. | |
@retval EFI_SUCCESS Execute the request action succss. | |
@retval EFI_INVALID_PARAMETER The input action value is invalid. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExecuteAction ( | |
IN UINT32 Action, | |
IN UINT16 DefaultId | |
) | |
{ | |
EFI_STATUS Status; | |
FORM_BROWSER_FORMSET *FormSet; | |
FORM_BROWSER_FORM *Form; | |
if (gBrowserSettingScope < SystemLevel && gCurrentSelection == NULL) { | |
return EFI_NOT_READY; | |
} | |
Status = EFI_SUCCESS; | |
FormSet = NULL; | |
Form = NULL; | |
if (gBrowserSettingScope < SystemLevel) { | |
FormSet = gCurrentSelection->FormSet; | |
Form = gCurrentSelection->Form; | |
} | |
// | |
// Executet the discard action. | |
// | |
if ((Action & BROWSER_ACTION_DISCARD) != 0) { | |
Status = DiscardForm (FormSet, Form, gBrowserSettingScope); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
// | |
// Executet the difault action. | |
// | |
if ((Action & BROWSER_ACTION_DEFAULT) != 0) { | |
Status = ExtractDefault (FormSet, Form, DefaultId, gBrowserSettingScope, GetDefaultForAll, NULL, FALSE, FALSE); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
UpdateStatementStatus (FormSet, Form, gBrowserSettingScope); | |
} | |
// | |
// Executet the submit action. | |
// | |
if ((Action & BROWSER_ACTION_SUBMIT) != 0) { | |
Status = SubmitForm (FormSet, Form, gBrowserSettingScope); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
// | |
// Executet the reset action. | |
// | |
if ((Action & BROWSER_ACTION_RESET) != 0) { | |
gResetRequired = TRUE; | |
} | |
// | |
// Executet the exit action. | |
// | |
if ((Action & BROWSER_ACTION_EXIT) != 0) { | |
DiscardForm (FormSet, Form, gBrowserSettingScope); | |
if (gBrowserSettingScope == SystemLevel) { | |
if (ExitHandlerFunction != NULL) { | |
ExitHandlerFunction (); | |
} | |
} | |
gExitRequired = TRUE; | |
} | |
return Status; | |
} | |
/** | |
Create reminder to let user to choose save or discard the changed browser data. | |
Caller can use it to actively check the changed browser data. | |
@retval BROWSER_NO_CHANGES No browser data is changed. | |
@retval BROWSER_SAVE_CHANGES The changed browser data is saved. | |
@retval BROWSER_DISCARD_CHANGES The changed browser data is discard. | |
@retval BROWSER_KEEP_CURRENT Browser keep current changes. | |
**/ | |
UINT32 | |
EFIAPI | |
SaveReminder ( | |
VOID | |
) | |
{ | |
LIST_ENTRY *Link; | |
FORM_BROWSER_FORMSET *FormSet; | |
BOOLEAN IsDataChanged; | |
UINT32 DataSavedAction; | |
UINT32 ConfirmRet; | |
DataSavedAction = BROWSER_NO_CHANGES; | |
IsDataChanged = FALSE; | |
Link = GetFirstNode (&gBrowserFormSetList); | |
while (!IsNull (&gBrowserFormSetList, Link)) { | |
FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); | |
Link = GetNextNode (&gBrowserFormSetList, Link); | |
if (!ValidateFormSet(FormSet)) { | |
continue; | |
} | |
if (IsNvUpdateRequiredForFormSet (FormSet)) { | |
IsDataChanged = TRUE; | |
break; | |
} | |
} | |
// | |
// No data is changed. No save is required. | |
// | |
if (!IsDataChanged) { | |
return DataSavedAction; | |
} | |
// | |
// If data is changed, prompt user to save or discard it. | |
// | |
do { | |
ConfirmRet = (UINT32) mFormDisplay->ConfirmDataChange(); | |
if (ConfirmRet == BROWSER_ACTION_SUBMIT) { | |
SubmitForm (NULL, NULL, SystemLevel); | |
DataSavedAction = BROWSER_SAVE_CHANGES; | |
break; | |
} else if (ConfirmRet == BROWSER_ACTION_DISCARD) { | |
DiscardForm (NULL, NULL, SystemLevel); | |
DataSavedAction = BROWSER_DISCARD_CHANGES; | |
break; | |
} else if (ConfirmRet == BROWSER_ACTION_NONE) { | |
DataSavedAction = BROWSER_KEEP_CURRENT; | |
break; | |
} | |
} while (1); | |
return DataSavedAction; | |
} | |
/** | |
Check whether the Reset Required for the browser | |
@retval TRUE Browser required to reset after exit. | |
@retval FALSE Browser not need to reset after exit. | |
**/ | |
BOOLEAN | |
EFIAPI | |
IsResetRequired ( | |
VOID | |
) | |
{ | |
return gResetRequired; | |
} | |