2300 lines
70 KiB
C
2300 lines
70 KiB
C
/** @file
|
|
Process BDS hot key.
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2016 - 2020, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include "Bds.h"
|
|
|
|
LIST_ENTRY mBdsHotKeyList = INITIALIZE_LIST_HEAD_VARIABLE (mBdsHotKeyList);
|
|
H2O_HOT_KEY_OPTION *mSelectedHotKeyOption = NULL;
|
|
|
|
STATIC H2O_KEY_DESC_PROTOCOL *mH2OKeyDesc = NULL;
|
|
STATIC BOOLEAN mHotKeySupport = FALSE;
|
|
STATIC BOOLEAN mHotKeyEnabled = TRUE;
|
|
STATIC EFI_EVENT mHotKeyTimeoutEvent = NULL;
|
|
STATIC UINTN mSimpleTextInCount = 0;
|
|
STATIC EFI_HANDLE *mSimpleTextInList = NULL;
|
|
|
|
typedef struct _BOTH_SIDE_SHIFT_STATE_INFO {
|
|
UINT32 RightShiftState;
|
|
UINT32 LeftShiftState;
|
|
} BOTH_SIDE_SHIFT_STATE_INFO;
|
|
STATIC BOTH_SIDE_SHIFT_STATE_INFO mBothSideShiftStateList[] = {{EFI_RIGHT_SHIFT_PRESSED , EFI_LEFT_SHIFT_PRESSED },
|
|
{EFI_RIGHT_CONTROL_PRESSED, EFI_LEFT_CONTROL_PRESSED},
|
|
{EFI_RIGHT_ALT_PRESSED , EFI_LEFT_ALT_PRESSED },
|
|
{EFI_RIGHT_LOGO_PRESSED , EFI_LEFT_LOGO_PRESSED },
|
|
};
|
|
|
|
STATIC
|
|
VOID
|
|
HotKeyRegisterKeyNotify (
|
|
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx
|
|
);
|
|
|
|
/**
|
|
Function to compare 2 value.
|
|
|
|
@param[in] Buffer1 Pointer to value to compare (UINTN*).
|
|
@param[in] Buffer2 Pointer to second value to compare (UINTN*).
|
|
|
|
@retval 0 Buffer1 equal to Buffer2.
|
|
@retval <0 Buffer1 is less than Buffer2.
|
|
@retval >0 Buffer1 is greater than Buffer2.
|
|
**/
|
|
STATIC
|
|
INTN
|
|
ValueCompare (
|
|
IN CONST VOID *Buffer1,
|
|
IN CONST VOID *Buffer2
|
|
)
|
|
{
|
|
if (*((UINTN *) Buffer1) == *((UINTN *) Buffer2)) {
|
|
return 0;
|
|
} else if (*((UINTN *) Buffer1) < *((UINTN *) Buffer2)) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Check if two key data are match.
|
|
|
|
@param[in] KeyData1 Pointer to key data 1
|
|
@param[in] KeyData2 Pointer to key data 2
|
|
|
|
@retval TRUE The two key data are match.
|
|
@retval FALSE The two key data are not match.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsKeyMatch (
|
|
IN EFI_KEY_DATA *KeyData1,
|
|
IN EFI_KEY_DATA *KeyData2
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT32 KeyData1ShiftState;
|
|
UINT32 KeyData2ShiftState;
|
|
UINT32 BothSideShiftState;
|
|
UINT32 KeyData1Result;
|
|
|
|
if ((KeyData1->Key.ScanCode != KeyData2->Key.ScanCode) ||
|
|
(KeyData1->Key.UnicodeChar != KeyData2->Key.UnicodeChar)) {
|
|
return FALSE;
|
|
}
|
|
|
|
KeyData1ShiftState = KeyData1->KeyState.KeyShiftState;
|
|
KeyData2ShiftState = KeyData2->KeyState.KeyShiftState;
|
|
|
|
if ((KeyData1ShiftState & EFI_MENU_KEY_PRESSED) != (KeyData2ShiftState & EFI_MENU_KEY_PRESSED)) {
|
|
return FALSE;
|
|
}
|
|
if ((KeyData1ShiftState & EFI_SYS_REQ_PRESSED) != (KeyData2ShiftState & EFI_SYS_REQ_PRESSED)) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (Index = 0; Index < sizeof(mBothSideShiftStateList) / sizeof(BOTH_SIDE_SHIFT_STATE_INFO); Index++) {
|
|
BothSideShiftState = (mBothSideShiftStateList[Index].RightShiftState | mBothSideShiftStateList[Index].LeftShiftState);
|
|
KeyData1Result = (KeyData1ShiftState & BothSideShiftState);
|
|
|
|
if (KeyData1Result == 0) {
|
|
if ((KeyData2ShiftState & BothSideShiftState) != 0) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
if ((KeyData2ShiftState & KeyData1Result) == 0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Return TRUE when the variable pointed by Name and Guid is a Key#### variable.
|
|
|
|
@param[in] VarName The name of the variable.
|
|
@param[in] VarGuid The GUID of the variable.
|
|
@param[out] KeyOptionNum Return the option number parsed from the Name.
|
|
|
|
@retval TRUE The variable pointed by Name and Guid is a Key#### variable.
|
|
@retval FALSE The variable pointed by Name and Guid isn't a Key#### variable.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsKeyOptionVariable (
|
|
IN CHAR16 *VarName,
|
|
IN EFI_GUID *VarGuid,
|
|
OUT UINT16 *KeyOptionNum
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
if (!CompareGuid (VarGuid, &gEfiGlobalVariableGuid) ||
|
|
(StrSize (VarName) != sizeof(L"Key####")) ||
|
|
(StrnCmp (VarName, L"Key", 3) != 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*KeyOptionNum = 0;
|
|
for (Index = 3; Index < 7; Index++) {
|
|
if ((VarName[Index] >= L'0') && (VarName[Index] <= L'9')) {
|
|
*KeyOptionNum = *KeyOptionNum * 16 + VarName[Index] - L'0';
|
|
} else if ((VarName[Index] >= L'A') && (VarName[Index] <= L'F')) {
|
|
*KeyOptionNum = *KeyOptionNum * 16 + VarName[Index] - L'A' + 10;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Check if the Key Option is valid or not.
|
|
|
|
@param[in] KeyOption The Key Option to be checked.
|
|
|
|
@retval TRUE The Key Option is valid.
|
|
@retval FALSE The Key Option is invalid.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsKeyOptionValid (
|
|
IN EFI_KEY_OPTION *KeyOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 BootOptionName[10];
|
|
UINT8 *BootOptionVar;
|
|
UINTN BootOptionSize;
|
|
UINT32 Crc;
|
|
|
|
//
|
|
// Check whether corresponding Boot Option exist
|
|
//
|
|
UnicodeSPrint (BootOptionName, sizeof(BootOptionName), L"Boot%04x", KeyOption->BootOption);
|
|
Status = CommonGetVariableDataAndSize (
|
|
BootOptionName,
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOptionSize,
|
|
(VOID **) &BootOptionVar
|
|
);
|
|
if (Status != EFI_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check CRC for Boot Option
|
|
//
|
|
gBS->CalculateCrc32 (BootOptionVar, BootOptionSize, &Crc);
|
|
FreePool (BootOptionVar);
|
|
|
|
return (BOOLEAN) ((KeyOption->BootOptionCrc == Crc) ? TRUE : FALSE);
|
|
}
|
|
|
|
/**
|
|
Check if the chord is already registered in hot key list.
|
|
|
|
@param[in] Chord Pointer to chord data.
|
|
|
|
@retval TRUE The chord is already registered.
|
|
@retval FALSE The chord is not registered.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsChordRegistered (
|
|
IN H2O_HOT_KEY_CHORD *Chord
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
UINT32 Index;
|
|
UINT32 KeyIndex;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
H2O_HOT_KEY_CHORD *HotKeyChord;
|
|
|
|
if (Chord == NULL || Chord->KeyCount == 0 || Chord->KeyList == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Link = GetFirstNode (&mBdsHotKeyList);
|
|
while (!IsNull (&mBdsHotKeyList, Link)) {
|
|
HotKeyOption = H2O_HOT_KEY_OPTION_FROM_LINK (Link);
|
|
Link = GetNextNode (&mBdsHotKeyList, Link);
|
|
|
|
for (Index = 0; Index < HotKeyOption->HotKeyChordCount; Index++) {
|
|
HotKeyChord = &HotKeyOption->HotKeyChordList[Index];
|
|
if (HotKeyChord->KeyCount != Chord->KeyCount) {
|
|
continue;
|
|
}
|
|
|
|
for (KeyIndex = 0; KeyIndex < Chord->KeyCount; KeyIndex++) {
|
|
if (!IsKeyMatch (&HotKeyChord->KeyList[KeyIndex], &Chord->KeyList[KeyIndex])) {
|
|
break;
|
|
}
|
|
}
|
|
if (KeyIndex == Chord->KeyCount) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Check if the device path belongs to pre-OS application.
|
|
|
|
@param[in] DevicePath Pointer to device path.
|
|
|
|
@retval TRUE It is the device path of pre-OS application.
|
|
@retval FALSE It is not the device path of pre-OS application.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsPreOsApp (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
if (DevicePath == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (DevicePath->Type == HARDWARE_DEVICE_PATH &&
|
|
DevicePath->SubType == HW_VENDOR_DP) {
|
|
return TRUE;
|
|
}
|
|
if (DevicePath->Type == MEDIA_DEVICE_PATH &&
|
|
DevicePath->SubType == MEDIA_PIWG_FW_FILE_DP &&
|
|
CompareGuid (&((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath)->FvFileName, &gH2OBdsLoadOptionSetupUtilityAppFileGuid)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Internal function to convert unicode string to ASCII string into the allocated buffer.
|
|
|
|
@param[in] String Pointer to unicode string
|
|
|
|
@return A pointer to the ASCII string or NULL if input parameter is NULL or allocate memory failed.
|
|
**/
|
|
STATIC
|
|
CHAR8 *
|
|
InternalUniStrToAsciiStrBuf (
|
|
IN CHAR16 *String
|
|
)
|
|
{
|
|
UINTN AsciiStringSize;
|
|
CHAR8 *AsciiString;
|
|
|
|
if (String == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
AsciiStringSize = (StrLen (String) + 1) * sizeof (CHAR8);
|
|
AsciiString = AllocateZeroPool (AsciiStringSize);
|
|
if (AsciiString != NULL) {
|
|
UnicodeStrToAsciiStrS (String, AsciiString, AsciiStringSize);
|
|
}
|
|
|
|
return AsciiString;
|
|
}
|
|
|
|
/**
|
|
Get all token numbers of specified token space GUID.
|
|
|
|
@param[in] TokenSpaceGuid The pointer of token space GUID.
|
|
@param[out] TokenNumList The double pointer to the token number list.
|
|
@param[out] TokenNumCount The pointer to the count of token number.
|
|
|
|
@retval EFI_SUCCESS Get all token numbers successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval EFI_OUT_OF_RESOURCES Allocate pool failed.
|
|
@retval EFI_NOT_FOUND There is no token number in specified token space GUID.
|
|
**/
|
|
EFI_STATUS
|
|
GetTokenNumList (
|
|
IN CONST GUID *TokenSpaceGuid,
|
|
OUT UINTN **TokenNumList,
|
|
OUT UINTN *TokenNumCount
|
|
)
|
|
{
|
|
UINTN Length;
|
|
UINTN Count;
|
|
UINTN *List;
|
|
UINTN TokenNum;
|
|
|
|
if (TokenSpaceGuid == NULL || TokenNumCount == NULL || TokenNumList == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Length = 0;
|
|
List = NULL;
|
|
TokenNum = 0;
|
|
Count = 0;
|
|
while (TRUE) {
|
|
TokenNum = LibPcdGetNextToken (TokenSpaceGuid, TokenNum);
|
|
if (TokenNum == 0) {
|
|
break;
|
|
}
|
|
|
|
if (Count >= Length) {
|
|
List = ReallocatePool (
|
|
sizeof(UINTN) * Length,
|
|
sizeof(UINTN) * (Length + 10),
|
|
List
|
|
);
|
|
if (List == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
Length += 10;
|
|
}
|
|
|
|
List[Count] = TokenNum;
|
|
Count++;
|
|
}
|
|
|
|
if (Count == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*TokenNumCount = Count;
|
|
*TokenNumList = List;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get all key option numbers (i.e. #### value of L"Key####" variable).
|
|
|
|
@param[out] KeyOptionNumList The double pointer to the key option number list.
|
|
@param[out] KeyOptionNumCount The pointer to the count of key option numbers
|
|
|
|
@retval EFI_SUCCESS Get all key option numbers successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval EFI_OUT_OF_RESOURCES Allocate pool failed.
|
|
@retval EFI_NOT_FOUND There is no key option variable in variable store.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetKeyOptionNumList (
|
|
OUT UINT16 **KeyOptionNumList,
|
|
OUT UINT16 *KeyOptionNumCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GUID VarGuid;
|
|
UINTN VarNameSize;
|
|
CHAR16 *VarName;
|
|
UINTN BuffeSize;
|
|
UINT16 OptionNumCount;
|
|
UINT16 *OptionNumList;
|
|
UINT16 OptionNum;
|
|
|
|
if (KeyOptionNumList == NULL || KeyOptionNumCount == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BuffeSize = sizeof(CHAR16) * 40;
|
|
VarName = AllocateZeroPool (BuffeSize);
|
|
if (VarName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
OptionNumCount = 0;
|
|
OptionNumList = NULL;
|
|
while (TRUE) {
|
|
VarNameSize = BuffeSize;
|
|
Status = gRT->GetNextVariableName (&VarNameSize, VarName, &VarGuid);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
VarName = ReallocatePool (BuffeSize, VarNameSize, VarName);
|
|
if (VarName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
BuffeSize = VarNameSize;
|
|
Status = gRT->GetNextVariableName (&VarNameSize, VarName, &VarGuid);
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
|
|
if (IsKeyOptionVariable (VarName ,&VarGuid, &OptionNum)) {
|
|
OptionNumList = ReallocatePool (
|
|
sizeof(UINT16) * OptionNumCount,
|
|
sizeof(UINT16) * (OptionNumCount + 1),
|
|
OptionNumList
|
|
);
|
|
if (OptionNumList == NULL) {
|
|
FreePool (VarName);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
OptionNumList[OptionNumCount] = OptionNum;
|
|
OptionNumCount++;
|
|
}
|
|
}
|
|
|
|
FreePool (VarName);
|
|
if (OptionNumCount == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*KeyOptionNumList = OptionNumList;
|
|
*KeyOptionNumCount = OptionNumCount;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get key data list by parsing chord string.
|
|
|
|
@param[in] ChordStr The pointer to the chord string.
|
|
@param[out] KeyList The double pointer to the key data list.
|
|
@param[out] KeyCount The pointer to the count of key data.
|
|
|
|
@retval EFI_SUCCESS Update key list successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval EFI_OUT_OF_RESOURCES Allocate pool failed.
|
|
@retval EFI_NOT_FOUND There is no valid key in chord string.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetKeyDataList (
|
|
IN CHAR16 *ChordStr,
|
|
OUT EFI_KEY_DATA **KeyList,
|
|
OUT UINT32 *KeyCount
|
|
)
|
|
{
|
|
CHAR16 *ChordStrBuffer;
|
|
CHAR16 *CurrKeyStr;
|
|
CHAR16 *NextKeyStr;
|
|
UINT32 Count;
|
|
EFI_KEY_DATA *List;
|
|
EFI_KEY_DATA KeyData;
|
|
EFI_STATUS Status;
|
|
|
|
if (ChordStr == NULL || *ChordStr == CHAR_NULL || KeyCount == NULL || KeyList == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ChordStrBuffer = AllocateCopyPool (StrSize (ChordStr), ChordStr);
|
|
if (ChordStrBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
List = NULL;
|
|
Count = 0;
|
|
NextKeyStr = ChordStrBuffer;
|
|
while (NextKeyStr != NULL) {
|
|
CurrKeyStr = NextKeyStr;
|
|
NextKeyStr = StrStr (NextKeyStr, H2O_HOT_KEY_KEY_SEPARATOR_STRING);
|
|
if (NextKeyStr != NULL) {
|
|
*NextKeyStr = CHAR_NULL;
|
|
NextKeyStr++;
|
|
}
|
|
|
|
Status = mH2OKeyDesc->KeyFromString (mH2OKeyDesc, CurrKeyStr, &KeyData);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
List = ReallocatePool (
|
|
sizeof(EFI_KEY_DATA) * Count,
|
|
sizeof(EFI_KEY_DATA) * (Count + 1),
|
|
List
|
|
);
|
|
if (List == NULL) {
|
|
FreePool (ChordStrBuffer);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (&List[Count], &KeyData, sizeof(EFI_KEY_DATA));
|
|
Count++;
|
|
}
|
|
FreePool (ChordStrBuffer);
|
|
|
|
if (Count == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*KeyCount = Count;
|
|
*KeyList = List;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get chord list by parsing chord ascii string.
|
|
|
|
@param[in] ChordAsciiStr The pointer to the chord ascii string.
|
|
@param[out] ChordList The double pointer to the chord list.
|
|
@param[out] ChordCount The pointer to the count of chord.
|
|
|
|
@retval EFI_SUCCESS Get chord list successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval EFI_OUT_OF_RESOURCES Allocate pool failed.
|
|
@retval EFI_NOT_FOUND There is no valid chord in hot key string.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetChordList (
|
|
IN CHAR8 *ChordAsciiStr,
|
|
OUT H2O_HOT_KEY_CHORD **ChordList,
|
|
OUT UINT32 *ChordCount
|
|
)
|
|
{
|
|
CHAR16 *ChordStr;
|
|
CHAR16 *CurrChordStr;
|
|
CHAR16 *NextChordStr;
|
|
UINT32 Count;
|
|
H2O_HOT_KEY_CHORD *List;
|
|
UINT32 KeyCount;
|
|
EFI_KEY_DATA *KeyList;
|
|
EFI_STATUS Status;
|
|
UINTN ChordAsciiStrSize;
|
|
|
|
if (ChordAsciiStr == NULL || *ChordAsciiStr == CHAR_NULL || ChordCount == NULL || ChordList == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ChordAsciiStrSize = AsciiStrSize (ChordAsciiStr);
|
|
ChordStr = AllocateZeroPool (ChordAsciiStrSize * sizeof(CHAR16));
|
|
if (ChordStr == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
AsciiStrToUnicodeStrS (ChordAsciiStr, ChordStr, ChordAsciiStrSize);
|
|
|
|
List = NULL;
|
|
Count = 0;
|
|
NextChordStr = ChordStr;
|
|
while (NextChordStr != NULL) {
|
|
CurrChordStr = NextChordStr;
|
|
NextChordStr = StrStr (NextChordStr, H2O_HOT_KEY_CHORD_SEPARATOR_STRING);
|
|
if (NextChordStr != NULL) {
|
|
*NextChordStr = CHAR_NULL;
|
|
NextChordStr++;
|
|
}
|
|
|
|
KeyCount = 0;
|
|
KeyList = NULL;
|
|
Status = GetKeyDataList (CurrChordStr, &KeyList, &KeyCount);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
List = ReallocatePool (
|
|
sizeof(H2O_HOT_KEY_CHORD) * Count,
|
|
sizeof(H2O_HOT_KEY_CHORD) * (Count + 1),
|
|
List
|
|
);
|
|
if (List == NULL) {
|
|
FreePool (ChordStr);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
List[Count].KeyCount = KeyCount;
|
|
List[Count].KeyList = KeyList;
|
|
List[Count].CurrentIndex = 0;
|
|
List[Count].ChordAsciiStr = InternalUniStrToAsciiStrBuf (CurrChordStr);
|
|
Count++;
|
|
}
|
|
FreePool (ChordStr);
|
|
|
|
if (Count == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*ChordCount = Count;
|
|
*ChordList = List;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get chord ASCII string.
|
|
|
|
@param[in] KeyList Pointer to the key list.
|
|
@param[in] KeyCount The count of key.
|
|
|
|
@return A pointer to the ASCII string or NULL if chord list is incorrect or allocate memory failed.
|
|
**/
|
|
STATIC
|
|
CHAR8 *
|
|
GetChordAsciiStr (
|
|
IN EFI_KEY_DATA *KeyList,
|
|
IN UINT32 KeyCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index;
|
|
CHAR16 *KeyStr;
|
|
CHAR16 *ChordStr;
|
|
CHAR16 *NewStr;
|
|
CHAR8 *ChordAsciiStr;
|
|
|
|
if (KeyList == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ChordStr = NULL;
|
|
for (Index = 0; Index < KeyCount; Index++) {
|
|
Status = mH2OKeyDesc->KeyToString (mH2OKeyDesc, &KeyList[Index], &KeyStr);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
if (ChordStr == NULL) {
|
|
ChordStr = KeyStr;
|
|
} else {
|
|
NewStr = CatSPrint (NULL, L"%s%s%s", ChordStr, H2O_HOT_KEY_KEY_SEPARATOR_STRING, KeyStr);
|
|
FreePool (KeyStr);
|
|
FreePool (ChordStr);
|
|
ChordStr = NewStr;
|
|
}
|
|
}
|
|
if (ChordStr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ChordAsciiStr = InternalUniStrToAsciiStrBuf (ChordStr);
|
|
FreePool (ChordStr);
|
|
return ChordAsciiStr;
|
|
}
|
|
|
|
/**
|
|
Get device path by parsing ascii string.
|
|
|
|
@param[in] DevicePathAsciiStr The pointer to the device path ascii string.
|
|
|
|
@return A pointer to the allocated device path or NULL if ascii string is NULL or allocate memory failed.
|
|
**/
|
|
EFI_DEVICE_PATH_PROTOCOL *
|
|
GetDevicePathByAsciiStr (
|
|
IN CHAR8 *DevicePathAsciiStr
|
|
)
|
|
{
|
|
CHAR16 *DevicePathStr;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
UINTN DevicePathAsciiStrSize;
|
|
|
|
if (DevicePathAsciiStr == NULL || *DevicePathAsciiStr == CHAR_NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
DevicePathAsciiStrSize = AsciiStrSize (DevicePathAsciiStr);
|
|
DevicePathStr = AllocateZeroPool (DevicePathAsciiStrSize * sizeof(CHAR16));
|
|
if (DevicePathStr == NULL) {
|
|
return NULL;
|
|
}
|
|
AsciiStrToUnicodeStrS (DevicePathAsciiStr, DevicePathStr, DevicePathAsciiStrSize);
|
|
|
|
DevicePath = GetDevicePathByStr (DevicePathStr);
|
|
FreePool (DevicePathStr);
|
|
|
|
return DevicePath;
|
|
}
|
|
|
|
/**
|
|
Get device path by parsing string.
|
|
|
|
@param[in] DevicePathStr The pointer to the device path string.
|
|
|
|
@return A pointer to the allocated device path or NULL if string is NULL or allocate memory failed.
|
|
**/
|
|
EFI_DEVICE_PATH_PROTOCOL *
|
|
GetDevicePathByStr (
|
|
IN CHAR16 *DevicePathStr
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *DevicePathFromText;
|
|
|
|
if (DevicePathStr == NULL || *DevicePathStr == CHAR_NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **) &DevicePathFromText);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
return DevicePathFromText->ConvertTextToDevicePath (DevicePathStr);
|
|
}
|
|
|
|
/**
|
|
Get current BDS context from BDS service function.
|
|
|
|
@param[out] BdsContext Pointer to the BDS context.
|
|
|
|
@retval EFI_SUCCESS Get current BDS context successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetBdsContext (
|
|
OUT H2O_BDS_CONTEXT *BdsContext
|
|
)
|
|
{
|
|
if (BdsContext == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ZeroMem (BdsContext, sizeof(H2O_BDS_CONTEXT));
|
|
|
|
BdsContext->Size = sizeof(H2O_BDS_CONTEXT);
|
|
gBdsServices->GetTimeout (gBdsServices, &BdsContext->Timeout);
|
|
gBdsServices->GetOsIndications (gBdsServices, &BdsContext->OsIndications, &BdsContext->OsIndicationsSupported);
|
|
gBdsServices->GetBootMode (gBdsServices, &BdsContext->BootMode);
|
|
gBdsServices->GetBootType (gBdsServices, &BdsContext->BootType);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get hot key option by handle.
|
|
|
|
@param[in] HotKeyHandle The pointer to hot key option handle.
|
|
|
|
@return A pointer to the hot key option or NULL if there is no match hot key option.
|
|
**/
|
|
H2O_HOT_KEY_OPTION *
|
|
GetHotKeyOption (
|
|
IN EFI_HANDLE HotKeyHandle
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
|
|
if (HotKeyHandle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Link = GetFirstNode (&mBdsHotKeyList);
|
|
while (!IsNull (&mBdsHotKeyList, Link)) {
|
|
HotKeyOption = H2O_HOT_KEY_OPTION_FROM_LINK (Link);
|
|
if ((EFI_HANDLE) HotKeyOption == HotKeyHandle) {
|
|
return HotKeyOption;
|
|
}
|
|
|
|
Link = GetNextNode (&mBdsHotKeyList, Link);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Free memory resource for chord list.
|
|
|
|
@param[in] ChordList A pointer to chord list
|
|
@param[in] ChordCount Number of chord in list
|
|
**/
|
|
VOID
|
|
FreeChordList (
|
|
IN H2O_HOT_KEY_CHORD *ChordList,
|
|
IN UINT32 ChordCount
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
|
|
if (ChordCount == 0 || ChordList == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (Index = 0; Index < ChordCount; Index++) {
|
|
if (ChordList[Index].KeyList != NULL) {
|
|
FreePool (ChordList[Index].KeyList);
|
|
}
|
|
if (ChordList[Index].ChordAsciiStr != NULL) {
|
|
FreePool (ChordList[Index].ChordAsciiStr);
|
|
}
|
|
}
|
|
FreePool (ChordList);
|
|
}
|
|
|
|
/**
|
|
Determine to enable or disable BDS hot key processing.
|
|
|
|
@param[in] Enable A Boolean that specifies whether hot key processing should be enabled (TRUE) or disabled (FALSE)
|
|
|
|
@return A Boolean indicates whether hot key processing was enabled (TRUE) or disabled (FALSE) before this call.
|
|
**/
|
|
BOOLEAN
|
|
EnableBdsHotKey (
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
gBdsServices->EnableHotKeys (gBdsServices, &Enable);
|
|
return Enable;
|
|
}
|
|
|
|
/**
|
|
Copy the input load option.
|
|
|
|
@param[in] SrcLoadOption A pointer to the load option.
|
|
|
|
@return A pointer to the allocated load option or NULL if input parameter is NULL or allocate memory failed.
|
|
**/
|
|
STATIC
|
|
H2O_BDS_LOAD_OPTION *
|
|
CopyLoadOption (
|
|
IN H2O_BDS_LOAD_OPTION *SrcLoadOption
|
|
)
|
|
{
|
|
H2O_BDS_LOAD_OPTION *DstLoadOption;
|
|
|
|
if (SrcLoadOption == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
DstLoadOption = AllocateCopyPool (sizeof(H2O_BDS_LOAD_OPTION), SrcLoadOption);
|
|
if (DstLoadOption == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (SrcLoadOption->DevicePath != NULL) {
|
|
DstLoadOption->DevicePath = AllocateCopyPool (GetDevicePathSize (SrcLoadOption->DevicePath), SrcLoadOption->DevicePath);
|
|
}
|
|
if (SrcLoadOption->Description != NULL) {
|
|
DstLoadOption->Description = AllocateCopyPool (StrSize (SrcLoadOption->Description), SrcLoadOption->Description);
|
|
}
|
|
if (SrcLoadOption->LoadOptionalDataSize != 0) {
|
|
DstLoadOption->LoadOptionalData = AllocateCopyPool (SrcLoadOption->LoadOptionalDataSize, SrcLoadOption->LoadOptionalData);
|
|
}
|
|
if (SrcLoadOption->StatusString != NULL) {
|
|
DstLoadOption->StatusString = AllocateCopyPool (StrSize (SrcLoadOption->StatusString), SrcLoadOption->StatusString);
|
|
}
|
|
|
|
return DstLoadOption;
|
|
}
|
|
|
|
/**
|
|
Lunch load option.
|
|
|
|
@param[in] LoadOption Pointer to load option.
|
|
|
|
@retval EFI_SUCCESS Perform lunch load option successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval Other LaunchLoadOption function return failed.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
LaunchLoadOption (
|
|
IN H2O_BDS_LOAD_OPTION *LoadOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN ExitDataSize;
|
|
CHAR16 *ExitData;
|
|
|
|
if (LoadOption == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
gBdsServices->ExpandLoadOption (gBdsServices, LoadOption);
|
|
|
|
if (DevicePathType (LoadOption->DevicePath) != BBS_DEVICE_PATH) {
|
|
BdsLibConnectDevicePath (LoadOption->DevicePath);
|
|
}
|
|
|
|
Status = gBdsServices->LaunchLoadOption (gBdsServices, LoadOption, &ExitDataSize, &ExitData);
|
|
if (Status != EFI_SUCCESS) {
|
|
BdsLibBootFailed (LoadOption, Status, ExitData, ExitDataSize);
|
|
} else {
|
|
BdsLibBootSuccess (LoadOption);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Lunch device path.
|
|
|
|
@param[in] DevicePath Pointer to device path
|
|
@param[in] OptionalData Pointer to optional data
|
|
@param[in] OptionalDataSize Optional data size
|
|
|
|
@retval EFI_SUCCESS Perform lunch load option successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval Other LaunchLoadOption function return failed.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
LaunchDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN UINT8 *OptionalData,
|
|
IN UINT32 OptionalDataSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
H2O_BDS_LOAD_OPTION *LoadOption;
|
|
UINT32 Attributes;
|
|
|
|
if (DevicePath == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Attributes = LOAD_OPTION_ACTIVE;
|
|
if (IsPreOsApp (DevicePath)) {
|
|
Attributes |= LOAD_OPTION_CATEGORY_APP;
|
|
}
|
|
|
|
Status = gBdsServices->CreateLoadOption (
|
|
gBdsServices,
|
|
BOOT_OPTION,
|
|
NULL,
|
|
NULL,
|
|
Attributes,
|
|
DevicePath,
|
|
NULL,
|
|
OptionalData,
|
|
OptionalDataSize,
|
|
&LoadOption
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = LaunchLoadOption (LoadOption);
|
|
gBdsServices->FreeLoadOption (gBdsServices, LoadOption);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Register a notification function for a particular keystroke for the input device.
|
|
It will separately register keystroke if key shift state contains both right and left states.
|
|
|
|
@param[in] SimpleTextInEx Protocol instance pointer.
|
|
@param[in] KeyData A pointer to a buffer that is filled in with the keystroke
|
|
information data for the key that was pressed.
|
|
@param[in] KeyNotifyFunction Points to the function to be called when the key sequence is typed specified by KeyData.
|
|
@param[out] NotifyHandle Points to the unique handle assigned to the registered notification.
|
|
|
|
@retval EFI_SUCCESS The notification function was registered successfully.
|
|
@retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data structures.
|
|
@retval EFI_INVALID_PARAMETER SimpleTextInEx, KeyData or NotifyHandle is NULL.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
SimpleTextInRegisterKeyNotify (
|
|
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx,
|
|
IN EFI_KEY_DATA *KeyData,
|
|
IN EFI_KEY_NOTIFY_FUNCTION KeyNotifyFunction,
|
|
OUT EFI_HANDLE *NotifyHandle
|
|
)
|
|
{
|
|
EFI_KEY_DATA *KeyDataList;
|
|
UINTN KeyDataCount;
|
|
UINTN Index;
|
|
UINTN InfoIndex;
|
|
UINT32 BothSideShiftState;
|
|
|
|
if (SimpleTextInEx == NULL || KeyData == NULL || KeyNotifyFunction == NULL || NotifyHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
KeyDataCount = 1;
|
|
KeyDataList = AllocateCopyPool (sizeof(EFI_KEY_DATA), KeyData);
|
|
if (KeyDataList == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Check if key shift state contains both right and left states. If yes, it should separately register.
|
|
//
|
|
for (InfoIndex = 0; InfoIndex < sizeof(mBothSideShiftStateList) / sizeof(BOTH_SIDE_SHIFT_STATE_INFO); InfoIndex++) {
|
|
BothSideShiftState = (mBothSideShiftStateList[InfoIndex].RightShiftState |
|
|
mBothSideShiftStateList[InfoIndex].LeftShiftState);
|
|
|
|
if ((KeyData->KeyState.KeyShiftState & BothSideShiftState) == BothSideShiftState) {
|
|
KeyDataList = ReallocatePool (
|
|
sizeof(EFI_KEY_DATA) * KeyDataCount,
|
|
sizeof(EFI_KEY_DATA) * KeyDataCount * 2,
|
|
KeyDataList
|
|
);
|
|
if (KeyDataList == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
CopyMem (&KeyDataList[KeyDataCount], KeyDataList, sizeof(EFI_KEY_DATA) * KeyDataCount);
|
|
|
|
for (Index = 0; Index < KeyDataCount; Index++) {
|
|
KeyDataList[Index].KeyState.KeyShiftState &= (~ mBothSideShiftStateList[InfoIndex].RightShiftState);
|
|
}
|
|
KeyDataCount *= 2;
|
|
for (; Index < KeyDataCount; Index++) {
|
|
KeyDataList[Index].KeyState.KeyShiftState &= (~ mBothSideShiftStateList[InfoIndex].LeftShiftState);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Index = 0; Index < KeyDataCount; Index++) {
|
|
KeyDataList[Index].KeyState.KeyToggleState = 0;
|
|
SimpleTextInEx->RegisterKeyNotify (
|
|
SimpleTextInEx,
|
|
&KeyDataList[Index],
|
|
KeyNotifyFunction,
|
|
NotifyHandle
|
|
);
|
|
}
|
|
|
|
FreePool (KeyDataList);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Call EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.Reset funtion to clean keyboard buffer.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
SimpleTextInResetKeyBuffer (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
gST->ConsoleInHandle,
|
|
&gEfiSimpleTextInputExProtocolGuid,
|
|
(VOID **) &SimpleTextInEx
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
SimpleTextInEx->Reset (SimpleTextInEx, FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Callback function for SimpleTextInEx protocol install events.
|
|
Register key notify when system detect a new input device.
|
|
|
|
@param[in] Event The event that is signaled.
|
|
@param[in] Context Pointer to the context data.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
SimpleTextInEventFn (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *HandleList;
|
|
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
UINTN Index;
|
|
UINTN RecordIndex;
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleTextInputExProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleList
|
|
);
|
|
if (EFI_ERROR (Status) || HandleCount == 0) {
|
|
return;
|
|
}
|
|
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
for (RecordIndex = 0; RecordIndex < mSimpleTextInCount; RecordIndex++) {
|
|
if ((UINTN) mSimpleTextInList[RecordIndex] == (UINTN) HandleList[Index]) {
|
|
break;
|
|
}
|
|
}
|
|
if (RecordIndex < mSimpleTextInCount) {
|
|
continue;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (HandleList[Index], &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
Status = gBS->HandleProtocol (HandleList[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &SimpleTextInEx);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Find a new simple text input device. Register key notify in this device.
|
|
//
|
|
HotKeyRegisterKeyNotify (SimpleTextInEx);
|
|
|
|
mSimpleTextInList = ReallocatePool (
|
|
sizeof(EFI_HANDLE) * mSimpleTextInCount,
|
|
sizeof(EFI_HANDLE) * (mSimpleTextInCount + 1),
|
|
mSimpleTextInList
|
|
);
|
|
if (mSimpleTextInList == NULL) {
|
|
mSimpleTextInCount = 0;
|
|
return;
|
|
}
|
|
mSimpleTextInList[mSimpleTextInCount] = HandleList[Index];
|
|
mSimpleTextInCount++;
|
|
}
|
|
|
|
FreePool (HandleList);
|
|
}
|
|
|
|
/**
|
|
Boot to boot option of PCD hot key.
|
|
|
|
@retval EFI_SUCCESS Boot to boot option successfully.
|
|
@retval Other Launch return failed.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
PcdHotKeyBoot (
|
|
IN H2O_HOT_KEY_OPTION *HotKeyOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS LaunchStatus;
|
|
H2O_BDS_CONTEXT BdsContext;
|
|
H2O_BDS_HOT_KEY_PROTOCOL *HotKeyProtocol;
|
|
|
|
LaunchStatus = EFI_NOT_FOUND;
|
|
|
|
if (!CompareGuid (&HotKeyOption->TypeValue.HotKeyProtocolGuid, &gZeroGuid)) {
|
|
Status = gBS->LocateProtocol (&HotKeyOption->TypeValue.HotKeyProtocolGuid, NULL, (VOID **) &HotKeyProtocol);
|
|
if (!EFI_ERROR (Status) &&
|
|
HotKeyProtocol != NULL &&
|
|
HotKeyProtocol->Size > (OFFSET_OF (H2O_BDS_HOT_KEY_PROTOCOL, Launch)) &&
|
|
HotKeyProtocol->Launch != NULL) {
|
|
GetBdsContext (&BdsContext);
|
|
LaunchStatus = HotKeyProtocol->Launch (
|
|
HotKeyProtocol,
|
|
HotKeyOption->Id,
|
|
HotKeyOption->Options,
|
|
&BdsContext
|
|
);
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (LaunchStatus) && HotKeyOption->DevicePath != NULL) {
|
|
LaunchStatus = LaunchDevicePath (
|
|
HotKeyOption->DevicePath,
|
|
(UINT8 *) HotKeyOption->Options,
|
|
(HotKeyOption->Options == NULL) ? 0 : (UINT32) AsciiStrSize (HotKeyOption->Options)
|
|
);
|
|
}
|
|
|
|
return LaunchStatus;
|
|
}
|
|
|
|
/**
|
|
Create H2O hot key option by PCD token number.
|
|
|
|
@param[in] PcdTokenNum Target hot key PCD token number.
|
|
|
|
@return Pointer to the H2O hot key option or NULL if PCD data is invalid or allocate memory failed.
|
|
**/
|
|
STATIC
|
|
H2O_HOT_KEY_OPTION *
|
|
PcdHotKeyCreateOption (
|
|
IN UINTN PcdTokenNum
|
|
)
|
|
{
|
|
PCD_H2O_HOT_KEY *PcdHotKey;
|
|
UINTN PcdHotKeySize;
|
|
UINT8 *PcdHotKeyPtr;
|
|
UINTN CurrentSize;
|
|
CHAR8 *KeyAsciiStr;
|
|
UINTN KeyAsciiStrSize;
|
|
CHAR8 *OptionAsciiStr;
|
|
UINTN OptionAsciiStrSize;
|
|
CHAR8 *DevPathAsciiStr;
|
|
EFI_STATUS Status;
|
|
UINT32 ChordCount;
|
|
H2O_HOT_KEY_CHORD *ChordList;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
|
|
PcdHotKey = (PCD_H2O_HOT_KEY *) LibPcdGetExPtr (&gH2OBdsHotKeyGuid, PcdTokenNum);
|
|
if (PcdHotKey == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Make sure PCD hot key data contain hot key string.
|
|
//
|
|
PcdHotKeySize = LibPcdGetExSize (&gH2OBdsHotKeyGuid, PcdTokenNum);
|
|
if (PcdHotKeySize <= (sizeof(PCD_H2O_HOT_KEY) + sizeof(CHAR8))) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Pasre PCD data by PCD_H2O_HOT_KEY definition.
|
|
//
|
|
PcdHotKeyPtr = ((UINT8 *) PcdHotKey) + sizeof(PCD_H2O_HOT_KEY);
|
|
CurrentSize = sizeof(PCD_H2O_HOT_KEY);
|
|
|
|
KeyAsciiStr = (CHAR8 *) PcdHotKeyPtr;
|
|
KeyAsciiStrSize = AsciiStrSize (KeyAsciiStr);
|
|
PcdHotKeyPtr += KeyAsciiStrSize;
|
|
CurrentSize += KeyAsciiStrSize;
|
|
|
|
OptionAsciiStr = (PcdHotKeySize > CurrentSize) ? (CHAR8 *) PcdHotKeyPtr : NULL;
|
|
OptionAsciiStrSize = (OptionAsciiStr != NULL) ? AsciiStrSize (OptionAsciiStr) : 0;
|
|
PcdHotKeyPtr += OptionAsciiStrSize;
|
|
CurrentSize += OptionAsciiStrSize;
|
|
|
|
DevPathAsciiStr = (PcdHotKeySize > CurrentSize) ? (CHAR8 *) PcdHotKeyPtr : NULL;
|
|
|
|
Status = GetChordList (KeyAsciiStr, &ChordList, &ChordCount);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
HotKeyOption = AllocateZeroPool (sizeof(H2O_HOT_KEY_OPTION));
|
|
if (HotKeyOption == NULL) {
|
|
FreeChordList (ChordList, ChordCount);
|
|
return NULL;
|
|
}
|
|
|
|
HotKeyOption->Signature = H2O_BDS_HOT_KEY_SIGNATURE;
|
|
HotKeyOption->Type = PCD_HOT_KEY;
|
|
HotKeyOption->PcdTokenNum = PcdTokenNum;
|
|
HotKeyOption->Id = PcdHotKey->Id;
|
|
HotKeyOption->HotKeyChordCount = ChordCount;
|
|
HotKeyOption->HotKeyChordList = ChordList;
|
|
HotKeyOption->Options = (OptionAsciiStr != NULL) ? AllocateCopyPool (OptionAsciiStrSize, OptionAsciiStr) : NULL;
|
|
HotKeyOption->DevicePath = (DevPathAsciiStr != NULL) ? GetDevicePathByAsciiStr (DevPathAsciiStr) : NULL;
|
|
CopyGuid (
|
|
&HotKeyOption->TypeValue.HotKeyProtocolGuid,
|
|
&PcdHotKey->HotKeyProtocolGuid
|
|
);
|
|
|
|
return HotKeyOption;
|
|
}
|
|
|
|
/**
|
|
Enumerate H2O BDS PCD hot key.
|
|
|
|
@retval EFI_SUCCESS Enumerate H2O BDS PCD hot key successfully.
|
|
@retval Other Fail to get H2O BDS PCD token number list.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
PcdHotKeyEnumeration (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN *TokenList;
|
|
UINTN TokenCount;
|
|
UINTN Index;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
|
|
Status = GetTokenNumList (&gH2OBdsHotKeyGuid, &TokenList, &TokenCount);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (TokenCount > 1) {
|
|
PerformQuickSort (TokenList, TokenCount, sizeof(UINTN), ValueCompare);
|
|
}
|
|
|
|
for (Index = 0; Index < TokenCount; Index++) {
|
|
HotKeyOption = PcdHotKeyCreateOption (TokenList[Index]);
|
|
if (HotKeyOption != NULL) {
|
|
InsertTailList (&mBdsHotKeyList, &HotKeyOption->Link);
|
|
}
|
|
}
|
|
|
|
if (TokenList != NULL) {
|
|
FreePool (TokenList);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Boot to boot option of UEFI hot key.
|
|
|
|
@retval EFI_SUCCESS Boot to boot option successfully.
|
|
@retval Other Covert variable to option failed.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UefiHotKeyBoot (
|
|
IN H2O_HOT_KEY_OPTION *HotKeyOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 BootOptionName[10];
|
|
H2O_BDS_LOAD_OPTION *LoadOption;
|
|
|
|
UnicodeSPrint (BootOptionName, sizeof(BootOptionName), L"Boot%04x", HotKeyOption->TypeValue.BootOptionNum);
|
|
Status = gBdsServices->ConvertVarToLoadOption (
|
|
gBdsServices,
|
|
BootOptionName,
|
|
&gEfiGlobalVariableGuid,
|
|
&LoadOption
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
LaunchLoadOption (LoadOption);
|
|
gBdsServices->FreeLoadOption (gBdsServices, LoadOption);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Create H2O hot key option from the UEFI key option.
|
|
|
|
@param[in] KeyOption The pointer to the UEFI key option.
|
|
|
|
@return Pointer to the H2O hot key option or NULL if allocate pool failed.
|
|
**/
|
|
STATIC
|
|
H2O_HOT_KEY_OPTION *
|
|
UefiHotKeyCreateOption (
|
|
IN EFI_KEY_OPTION *KeyOption
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_BOOT_KEY_DATA KeyData;
|
|
UINT32 KeyShiftState;
|
|
EFI_INPUT_KEY *InputKey;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
H2O_HOT_KEY_CHORD *HotKeyChord;
|
|
|
|
if (KeyOption == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Map key shift state from KeyOptions to EFI_KEY_DATA.KeyState
|
|
//
|
|
KeyData = KeyOption->KeyData;
|
|
|
|
KeyShiftState = EFI_SHIFT_STATE_VALID;
|
|
KeyShiftState |= (KeyData.Options.ShiftPressed ? (EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED ) : 0);
|
|
KeyShiftState |= (KeyData.Options.ControlPressed ? (EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) : 0);
|
|
KeyShiftState |= (KeyData.Options.AltPressed ? (EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED ) : 0);
|
|
KeyShiftState |= (KeyData.Options.LogoPressed ? (EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED ) : 0);
|
|
KeyShiftState |= (KeyData.Options.MenuPressed ? EFI_MENU_KEY_PRESSED : 0);
|
|
KeyShiftState |= (KeyData.Options.SysReqPressed ? EFI_SYS_REQ_PRESSED : 0);
|
|
|
|
HotKeyOption = AllocateZeroPool (sizeof(H2O_HOT_KEY_OPTION));
|
|
if (HotKeyOption == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
HotKeyOption->Signature = H2O_BDS_HOT_KEY_SIGNATURE;
|
|
HotKeyOption->Type = UEFI_HOT_KEY;
|
|
HotKeyOption->TypeValue.BootOptionNum = KeyOption->BootOption;
|
|
HotKeyOption->Options = NULL;
|
|
HotKeyOption->HotKeyChordCount = 1;
|
|
HotKeyOption->HotKeyChordList = AllocateZeroPool (HotKeyOption->HotKeyChordCount * sizeof(H2O_HOT_KEY_CHORD));
|
|
if (HotKeyOption->HotKeyChordList == NULL) {
|
|
FreePool (HotKeyOption);
|
|
return NULL;
|
|
}
|
|
|
|
HotKeyChord = HotKeyOption->HotKeyChordList;
|
|
HotKeyChord->KeyCount = (UINT32) KeyData.Options.InputKeyCount;
|
|
HotKeyChord->KeyList = AllocateZeroPool (HotKeyChord->KeyCount * sizeof(EFI_KEY_DATA));
|
|
if (HotKeyChord->KeyList == NULL) {
|
|
FreePool (HotKeyOption->HotKeyChordList);
|
|
FreePool (HotKeyOption);
|
|
return NULL;
|
|
}
|
|
|
|
Index = 0;
|
|
InputKey = (EFI_INPUT_KEY *) (((UINT8 *) KeyOption) + sizeof(EFI_KEY_OPTION));
|
|
do {
|
|
//
|
|
// If Key CodeCount is 0, then only KeyData[0] is used;
|
|
// if Key CodeCount is n, then KeyData[0]~KeyData[n-1] are used
|
|
//
|
|
HotKeyChord->KeyList[Index].Key.ScanCode = InputKey[Index].ScanCode;
|
|
HotKeyChord->KeyList[Index].Key.UnicodeChar = InputKey[Index].UnicodeChar;
|
|
HotKeyChord->KeyList[Index].KeyState.KeyShiftState = KeyShiftState;
|
|
|
|
Index++;
|
|
} while (Index < HotKeyChord->KeyCount);
|
|
HotKeyChord->ChordAsciiStr = GetChordAsciiStr (HotKeyChord->KeyList, HotKeyChord->KeyCount);
|
|
|
|
return HotKeyOption;
|
|
}
|
|
|
|
/**
|
|
Enumerate UEFI hot key.
|
|
|
|
@retval EFI_SUCCESS Enumerate UEFI hot key successfully.
|
|
@retval Other Fail to get UEFI hot key list.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UefiHotKeyEnumeration (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 *KeyOptionNumList;
|
|
UINT16 KeyOptionNumCount;
|
|
UINT16 Index;
|
|
CHAR16 KeyOptionName[8];
|
|
EFI_KEY_OPTION *KeyOption;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
|
|
//
|
|
// Enumerate Key#### variable and add it in hot key list if verification is success.
|
|
//
|
|
KeyOptionNumList = NULL;
|
|
KeyOptionNumCount = 0;
|
|
Status = GetKeyOptionNumList (&KeyOptionNumList, &KeyOptionNumCount);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
for (Index = 0; Index < KeyOptionNumCount; Index++) {
|
|
UnicodeSPrint (KeyOptionName, sizeof(KeyOptionName), L"Key%04x", (UINT16) KeyOptionNumList[Index]);
|
|
KeyOption = CommonGetVariableData (KeyOptionName, &gEfiGlobalVariableGuid);
|
|
if (KeyOption == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (IsKeyOptionValid (KeyOption)) {
|
|
HotKeyOption = UefiHotKeyCreateOption (KeyOption);
|
|
if (HotKeyOption != NULL) {
|
|
InsertTailList (&mBdsHotKeyList, &HotKeyOption->Link);
|
|
}
|
|
}
|
|
|
|
FreePool (KeyOption);
|
|
}
|
|
|
|
FreePool (KeyOptionNumList);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Set BootOptionSupport variable to export the capability of UEFI hot key.
|
|
|
|
@retval EFI_SUCCESS Set BootOptionSupport variable successfully.
|
|
@retval Other Set BootOptionSupport variable failed.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UefiHotKeyExportCapability (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 BootOptionSupport;
|
|
UINTN Size;
|
|
|
|
//
|
|
// Export our capability - EFI_BOOT_OPTION_SUPPORT_KEY with maximum number of key presses of 3
|
|
//
|
|
Size = sizeof(BootOptionSupport);
|
|
Status = gRT->GetVariable (
|
|
EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&Size,
|
|
&BootOptionSupport
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
BootOptionSupport = 0;
|
|
}
|
|
BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY;
|
|
SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3);
|
|
|
|
return gRT->SetVariable (
|
|
EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
sizeof(BootOptionSupport),
|
|
&BootOptionSupport
|
|
);
|
|
}
|
|
|
|
/**
|
|
Internal function to initialize H2O_BDS_CP_HOT_KEY_DETECTED_DATA data and trigger PcdH2OBdsCpHotKeyDetectedSupported checkpoint.
|
|
|
|
@retval EFI_SUCCESS Trigger PcdH2OBdsCpHotKeyDetectedSupported checkpoint successfully.
|
|
@return Other Other error occurred while triggering PcdH2OBdsCpHotKeyDetectedSupported checkpoint.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
TriggerCpHotKeyDetected (
|
|
IN CHAR8 *HotKeyAsciiStr
|
|
)
|
|
{
|
|
H2O_BDS_CP_HOT_KEY_DETECTED_DATA HotKeyDetectedData;
|
|
EFI_STATUS Status;
|
|
|
|
HotKeyDetectedData.Size = sizeof (H2O_BDS_CP_HOT_KEY_DETECTED_DATA);
|
|
HotKeyDetectedData.Status = H2O_CP_TASK_NORMAL;
|
|
HotKeyDetectedData.HotKey = HotKeyAsciiStr;
|
|
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OBdsCpHotKeyDetectedGuid));
|
|
Status = H2OCpTrigger (&gH2OBdsCpHotKeyDetectedGuid, &HotKeyDetectedData);
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", HotKeyDetectedData.Status));
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Reset all hot key option in the list.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
HotKeyResetHotKeyList (
|
|
VOID
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
UINT32 Index;
|
|
|
|
Link = GetFirstNode (&mBdsHotKeyList);
|
|
while (!IsNull (&mBdsHotKeyList, Link)) {
|
|
HotKeyOption = H2O_HOT_KEY_OPTION_FROM_LINK (Link);
|
|
Link = GetNextNode (&mBdsHotKeyList, Link);
|
|
|
|
for (Index = 0; Index < HotKeyOption->HotKeyChordCount; Index++) {
|
|
HotKeyOption->HotKeyChordList[Index].CurrentIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Registered key notify function to check if hot key is pressed.
|
|
|
|
@param[in] KeyData Pointer to buffer that is filled in with the keystroke information for the key that was pressed.
|
|
|
|
@return EFI_SUCCESS KeyData is successfully processed.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
HotKeyKeyNotifyFunc (
|
|
IN EFI_KEY_DATA *KeyData
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
UINT32 Index;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
BOOLEAN HotKeyFound;
|
|
H2O_HOT_KEY_CHORD *HotKeyChord;
|
|
EFI_KEY_DATA *ChordKeyData;
|
|
CHAR8 *HotKeyAsciiStr;
|
|
|
|
if (!mHotKeyEnabled) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (mSelectedHotKeyOption != NULL) {
|
|
//
|
|
// Do not process sequential hotkey stroke until the previous selected hot key is processed.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
HotKeyAsciiStr = NULL;
|
|
|
|
Link = GetFirstNode (&mBdsHotKeyList);
|
|
while (!IsNull (&mBdsHotKeyList, Link)) {
|
|
HotKeyOption = H2O_HOT_KEY_OPTION_FROM_LINK (Link);
|
|
Link = GetNextNode (&mBdsHotKeyList, Link);
|
|
|
|
for (Index = 0; Index < HotKeyOption->HotKeyChordCount; Index++) {
|
|
HotKeyFound = FALSE;
|
|
HotKeyChord = &HotKeyOption->HotKeyChordList[Index];
|
|
ChordKeyData = &HotKeyChord->KeyList[HotKeyChord->CurrentIndex];
|
|
|
|
if (IsKeyMatch (ChordKeyData, KeyData)) {
|
|
//
|
|
// For hotkey of key combination, transit to next waiting state
|
|
//
|
|
HotKeyChord->CurrentIndex++;
|
|
|
|
if (HotKeyChord->CurrentIndex == HotKeyChord->KeyCount) {
|
|
//
|
|
// Received the whole key stroke sequence
|
|
//
|
|
HotKeyFound = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// Receive an unexpected key stroke, reset to initial waiting state
|
|
//
|
|
HotKeyChord->CurrentIndex = 0;
|
|
}
|
|
|
|
if (HotKeyFound) {
|
|
//
|
|
// Reset to initial waiting state
|
|
//
|
|
HotKeyChord->CurrentIndex = 0;
|
|
|
|
//
|
|
// Launch its BootOption
|
|
//
|
|
mSelectedHotKeyOption = HotKeyOption;
|
|
HotKeyAsciiStr = HotKeyChord->ChordAsciiStr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mSelectedHotKeyOption != NULL) {
|
|
if (FeaturePcdGet (PcdH2OBdsCpHotKeyDetectedSupported)) {
|
|
TriggerCpHotKeyDetected (HotKeyAsciiStr);
|
|
}
|
|
if (BdsLibIsBootOrderHookEnabled ()) {
|
|
BdsLibRestoreBootOrderFromPhysicalBootOrder ();
|
|
}
|
|
HotKeyResetHotKeyList ();
|
|
DisplayHotKeyAfterUserSelection ();
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Register the hot key notify function to given SimpleTextInEx protocol instance.
|
|
|
|
@param[in] SimpleTextInEx Simple Text Input Ex protocol instance
|
|
**/
|
|
STATIC
|
|
VOID
|
|
HotKeyRegisterKeyNotify (
|
|
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
UINTN HotKeyIndex;
|
|
UINTN Index;
|
|
H2O_HOT_KEY_CHORD *HotKeyChord;
|
|
VOID *NotifyHandle;
|
|
|
|
Link = GetFirstNode (&mBdsHotKeyList);
|
|
while (!IsNull (&mBdsHotKeyList, Link)) {
|
|
HotKeyOption = H2O_HOT_KEY_OPTION_FROM_LINK (Link);
|
|
Link = GetNextNode (&mBdsHotKeyList, Link);
|
|
|
|
for (HotKeyIndex = 0; HotKeyIndex < HotKeyOption->HotKeyChordCount; HotKeyIndex++) {
|
|
HotKeyChord = &HotKeyOption->HotKeyChordList[HotKeyIndex];
|
|
|
|
for (Index = 0; Index < HotKeyChord->KeyCount; Index++) {
|
|
SimpleTextInRegisterKeyNotify (
|
|
SimpleTextInEx,
|
|
&HotKeyChord->KeyList[Index],
|
|
HotKeyKeyNotifyFunc,
|
|
&NotifyHandle
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Register key notification in simple text input device for input hot key option.
|
|
|
|
@param[in] HotKeyOption Pointer to hot key option
|
|
**/
|
|
STATIC
|
|
VOID
|
|
HotKeyRegisterOptionHotKey (
|
|
IN H2O_HOT_KEY_OPTION *HotKeyOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
|
|
UINTN Index;
|
|
UINTN ChordIndex;
|
|
UINTN KeyIndex;
|
|
H2O_HOT_KEY_CHORD *Chord;
|
|
VOID *NotifyHandle;
|
|
|
|
for (Index = 0; Index < mSimpleTextInCount; Index++) {
|
|
Status = gBS->HandleProtocol (mSimpleTextInList[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &SimpleTextInEx);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
for (ChordIndex = 0; ChordIndex < HotKeyOption->HotKeyChordCount; ChordIndex++) {
|
|
Chord = &HotKeyOption->HotKeyChordList[ChordIndex];
|
|
|
|
for (KeyIndex = 0; KeyIndex < Chord->KeyCount; KeyIndex++) {
|
|
SimpleTextInRegisterKeyNotify (
|
|
SimpleTextInEx,
|
|
&Chord->KeyList[KeyIndex],
|
|
HotKeyKeyNotifyFunc,
|
|
&NotifyHandle
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Callback function to disable BDS hot key functionality
|
|
|
|
@param[in] Event The event that is signaled.
|
|
@param[in] Context Pointer to the context data.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
HotKeyStopHotKeyEventtFn (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Instance;
|
|
|
|
Status = gBS->LocateProtocol (&gH2OStopHotKeyGuid, NULL, (VOID **) &Instance);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
EnableBdsHotKey (FALSE);
|
|
gBS->CloseEvent (Event);
|
|
}
|
|
|
|
/**
|
|
Turn on hot ket detection at Boot after check point.
|
|
|
|
@param[in] Event The Event this notify function registered to.
|
|
@param[in] Handle The handle associated with a previously registered checkpoint handler.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
HotKeyBootAfterCpHandler (
|
|
IN EFI_EVENT Event,
|
|
IN H2O_CP_HANDLE Handle
|
|
)
|
|
{
|
|
EnableBdsHotKey (TRUE);
|
|
H2OCpUnregisterHandler (Handle);
|
|
}
|
|
|
|
/**
|
|
Turn off hot key detection at Boot before check point.
|
|
|
|
@param[in] Event The Event this notify function registered to.
|
|
@param[in] Handle The handle associated with a previously registered checkpoint handler.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
HotKeyBootBeforeCpHandler (
|
|
IN EFI_EVENT Event,
|
|
IN H2O_CP_HANDLE Handle
|
|
)
|
|
{
|
|
BOOLEAN PreviousValue;
|
|
H2O_CP_HANDLE CpHandle;
|
|
EFI_STATUS Status;
|
|
|
|
PreviousValue = EnableBdsHotKey (FALSE);
|
|
if (FeaturePcdGet (PcdH2OBdsCpBootAfterSupported) && PreviousValue) {
|
|
//
|
|
// Previous hot key status is enabled, it should restore hot key status to enabled after this boot.
|
|
//
|
|
Status = H2OCpRegisterHandler (&gH2OBdsCpBootAfterGuid , HotKeyBootAfterCpHandler , H2O_CP_MEDIUM, &CpHandle);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpBootAfterGuid, Status));
|
|
return;
|
|
}
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpBootAfterGuid, Status));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Based on Timeout variable to register timer event. Guarantee that user have enough time to press hot key.
|
|
|
|
@param[in] Event The Event this notify function registered to.
|
|
@param[in] Handle The handle associated with a previously registered checkpoint handler.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
HotKeyDisplayBeforeCpHandler (
|
|
IN EFI_EVENT Event,
|
|
IN H2O_CP_HANDLE Handle
|
|
)
|
|
{
|
|
UINT16 Timeout;
|
|
EFI_STATUS Status;
|
|
UINTN Size;
|
|
UINT64 TimeOutMs;
|
|
|
|
if (!mHotKeyEnabled || mHotKeyTimeoutEvent != NULL) {
|
|
return;
|
|
}
|
|
|
|
Size = sizeof (Timeout);
|
|
Status = gRT->GetVariable (L"Timeout", &gEfiGlobalVariableGuid, NULL, &Size, &Timeout);
|
|
if (EFI_ERROR(Status)) {
|
|
Timeout = 0;
|
|
}
|
|
|
|
TimeOutMs = MultU64x32 ((UINT64) Timeout, 1000) + PcdGet32 (PcdPlatformBootTimeOutMs);
|
|
if (TimeOutMs == 0) {
|
|
return;
|
|
}
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER,
|
|
TPL_NOTIFY,
|
|
NULL,
|
|
NULL,
|
|
&mHotKeyTimeoutEvent
|
|
);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = gBS->SetTimer (mHotKeyTimeoutEvent, TimerRelative, MultU64x32 (TimeOutMs, TIMER_EVENT_ONE_MILLISEC));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Initialize BDS hot key.
|
|
|
|
@retval EFI_SUCCESS Initialize BDS hot key successfully.
|
|
@retval Other Locate protocol failed.
|
|
**/
|
|
EFI_STATUS
|
|
BdsHotKeyInit (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *Registration;
|
|
H2O_CP_HANDLE CpHandle;
|
|
|
|
Status = gBS->LocateProtocol (&gH2OKeyDescProtocolGuid, NULL, (VOID **) &mH2OKeyDesc);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
mHotKeySupport = TRUE;
|
|
UefiHotKeyExportCapability ();
|
|
|
|
UefiHotKeyEnumeration ();
|
|
PcdHotKeyEnumeration ();
|
|
|
|
BdsHotKeyDescInit ();
|
|
|
|
//
|
|
// Register protocol notify to register key notify when system detects a new input device.
|
|
//
|
|
EfiCreateProtocolNotifyEvent (
|
|
&gEfiSimpleTextInputExProtocolGuid,
|
|
TPL_CALLBACK,
|
|
SimpleTextInEventFn,
|
|
NULL,
|
|
&Registration
|
|
);
|
|
|
|
//
|
|
// Register protocol notify to disable hot key detection for compatibility.
|
|
//
|
|
EfiCreateProtocolNotifyEvent (
|
|
&gH2OStopHotKeyGuid,
|
|
TPL_CALLBACK,
|
|
HotKeyStopHotKeyEventtFn,
|
|
NULL,
|
|
&Registration
|
|
);
|
|
|
|
if (FeaturePcdGet (PcdH2OBdsCpDisplayBeforeSupported)) {
|
|
//
|
|
// Register display before check point to record the start time.
|
|
// In order to make sure that user have enough time (defined by Timeout variable) to press hot key.
|
|
// Register a timer event at display before checkpoint and check the signal status of event in BdsHotKeyBoot().
|
|
// Guarantee that the period of display before checkpoint to BdsHotKeyBoot() exceeds Timeout variable value.
|
|
//
|
|
Status = H2OCpRegisterHandler (
|
|
&gH2OBdsCpDisplayBeforeGuid,
|
|
HotKeyDisplayBeforeCpHandler,
|
|
H2O_CP_MEDIUM,
|
|
&CpHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpDisplayBeforeGuid, Status));
|
|
return Status;
|
|
}
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpDisplayBeforeGuid, Status));
|
|
}
|
|
if (FeaturePcdGet (PcdH2OBdsCpBootBeforeSupported)) {
|
|
//
|
|
// Register boot before check point to turn off hot key detection.
|
|
//
|
|
Status = H2OCpRegisterHandler (
|
|
&gH2OBdsCpBootBeforeGuid,
|
|
HotKeyBootBeforeCpHandler,
|
|
H2O_CP_MEDIUM,
|
|
&CpHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpBootBeforeGuid, Status));
|
|
return Status;
|
|
}
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpBootBeforeGuid, Status));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Boot to hot key option if there is one option which key is detected.
|
|
|
|
@param[in] TriggerPoint Trigger point determine what kinds of hot keys are permitted to be performed.
|
|
|
|
@retval EFI_SUCCESS Boot to hot key option successfully.
|
|
@retval EFI_NOT_FOUND There is no hot key which ket is detected.
|
|
@retval EFI_NOT_READY Pressed hot key is detected but the option is not permitted to be performed now.
|
|
@retval EFI_UNSUPPORTED Hot key type is unsupport.
|
|
@retval Other Boot to hot key option failed.
|
|
**/
|
|
EFI_STATUS
|
|
BdsHotKeyBoot (
|
|
IN UINT32 TriggerPoint
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
H2O_BDS_CONTEXT BdsContext;
|
|
BOOLEAN PreviousValue;
|
|
|
|
if (!mHotKeySupport) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Make sure user has enough time (defined by Timeout variable) to press hot key.
|
|
//
|
|
while (mHotKeyTimeoutEvent != NULL) {
|
|
Status = gBS->CheckEvent (mHotKeyTimeoutEvent);
|
|
if (mSelectedHotKeyOption != NULL || IsHotKeyDetected () || Status != EFI_NOT_READY) {
|
|
gBS->CloseEvent (mHotKeyTimeoutEvent);
|
|
mHotKeyTimeoutEvent = NULL;
|
|
} else {
|
|
gBS->Stall (500);
|
|
}
|
|
}
|
|
|
|
if (mSelectedHotKeyOption == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Clean keyboard buffer to make sure previous key data doesn't impact on the launching function of hot key.
|
|
//
|
|
SimpleTextInResetKeyBuffer ();
|
|
|
|
PreviousValue = EnableBdsHotKey (FALSE);
|
|
switch (mSelectedHotKeyOption->Type) {
|
|
|
|
case UEFI_HOT_KEY:
|
|
if ((TriggerPoint & TRIGGER_POINT_BEFORE_CHECK_EACH_BOOT_OPTION) == 0) {
|
|
EnableBdsHotKey (PreviousValue);
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
Status = UefiHotKeyBoot (mSelectedHotKeyOption);
|
|
break;
|
|
|
|
case PCD_HOT_KEY:
|
|
Status = PcdHotKeyBoot (mSelectedHotKeyOption);
|
|
break;
|
|
|
|
case REGISTERED_CALLBACK_HOT_KEY:
|
|
GetBdsContext (&BdsContext);
|
|
Status = mSelectedHotKeyOption->TypeValue.HotKeyFn (
|
|
NULL,
|
|
mSelectedHotKeyOption->Id,
|
|
mSelectedHotKeyOption->Options,
|
|
&BdsContext
|
|
);
|
|
|
|
break;
|
|
|
|
case REGISTERED_LOAD_OPTION_HOT_KEY:
|
|
Status = LaunchLoadOption (mSelectedHotKeyOption->TypeValue.LoadOption);
|
|
break;
|
|
|
|
default:
|
|
Status = EFI_UNSUPPORTED;
|
|
break;
|
|
}
|
|
EnableBdsHotKey (PreviousValue);
|
|
|
|
mSelectedHotKeyOption = NULL;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Enable or disable BDS detection of hot keys.
|
|
While enabled, the Boot Manager will continue to check each key pressed against the list of registered hot keys.
|
|
When disabled, any hot key chord detect is suspended and any in-process hot key chords wil be discarded.
|
|
|
|
@param[in] This A Pointer to current instance of this protocol.
|
|
@param[out] Enable On entry, a pointer to a Boolean that specifies whether hot key processing should be
|
|
enabled (TRUE) or disabled (FALSE). On exit, a pointer to a Boolean that indicates
|
|
whether hot key processing was enabled (TRUE) or disabled (FALSE) before this call.
|
|
|
|
@retval EFI_SUCCESS This function completed successfully.
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BdsServicesEnableHotKeys (
|
|
IN H2O_BDS_SERVICES_PROTOCOL *This,
|
|
IN OUT BOOLEAN *Enable
|
|
)
|
|
{
|
|
BOOLEAN OrgEnable;
|
|
|
|
if (!mHotKeySupport) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
if (This == NULL || Enable == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OrgEnable = mHotKeyEnabled;
|
|
mHotKeyEnabled = *Enable;
|
|
|
|
if (OrgEnable && !mHotKeyEnabled) {
|
|
HotKeyResetHotKeyList ();
|
|
}
|
|
|
|
*Enable = OrgEnable;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Register a function to be called when a specified hot key chord is detected.
|
|
|
|
@param[in] This A pointer to the current instance of this protocol.
|
|
@param[in] HotKey A pointer to a null-terminated ASCII string that specifies one or more hot key chords
|
|
for this hot key.
|
|
@param[in] HotKeyFn Pointer to the function that will be called when the hot key chord specified by
|
|
HotKey is detected.
|
|
@param[in] Id Unsigned integer associated with this specific hot key.
|
|
@param[in] Options Optional pointer to a null-terminated ASCII string that contains additional operating
|
|
parameters.
|
|
@param[out] HotKeyHandle On return, a pointer to a handle that uniquely specifies this hot key.
|
|
|
|
@retval EFI_SUCCESS Hot key registered.
|
|
@retval EFI_INVALID_PARAMETER The hot key string was NULL, or had an invalid format.
|
|
The hot key attributes had bits set that are unsupported.
|
|
@retval EFI_ALREADY_STARTED A hot key with one of the specified hot key chords has already been registered.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BdsServicesRegisterCallbackHotKey (
|
|
IN H2O_BDS_SERVICES_PROTOCOL *This,
|
|
IN CONST CHAR8 *HotKey,
|
|
IN H2O_BDS_HOT_KEY_LAUNCH HotKeyFn,
|
|
IN UINT32 Id,
|
|
IN CONST CHAR8 *Options OPTIONAL,
|
|
OUT EFI_HANDLE *HotKeyHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index;
|
|
UINT32 ChordCount;
|
|
H2O_HOT_KEY_CHORD *ChordList;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
|
|
if (!mHotKeySupport) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
if (This == NULL || HotKey == NULL || HotKeyFn == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = GetChordList ((CHAR8 *) HotKey, &ChordList, &ChordCount);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
for (Index = 0; Index < ChordCount; Index++) {
|
|
if (IsChordRegistered (&ChordList[Index])) {
|
|
DEBUG((EFI_D_INFO, "BdsHotKey: Number %d of hot key (%a) is already registered before.\n", Index + 1, HotKey));
|
|
FreePool (ChordList);
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
}
|
|
|
|
HotKeyOption = AllocateZeroPool (sizeof(H2O_HOT_KEY_OPTION));
|
|
if (HotKeyOption == NULL) {
|
|
FreePool (ChordList);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
HotKeyOption->Signature = H2O_BDS_HOT_KEY_SIGNATURE;
|
|
HotKeyOption->Type = REGISTERED_CALLBACK_HOT_KEY;
|
|
HotKeyOption->TypeValue.HotKeyFn = HotKeyFn;
|
|
HotKeyOption->Id = Id;
|
|
HotKeyOption->HotKeyChordCount = ChordCount;
|
|
HotKeyOption->HotKeyChordList = ChordList;
|
|
HotKeyOption->Options = (Options != NULL) ? AllocateCopyPool (AsciiStrSize (Options), Options) : NULL;
|
|
CopyMem (&HotKeyOption->Description , &mDefaultHotKeyDescPosn , sizeof(H2O_HOT_KEY_DESCRIPTION));
|
|
CopyMem (&HotKeyOption->QuietDescription, &mDefaultHotKeyDescQuietPosn, sizeof(H2O_HOT_KEY_DESCRIPTION));
|
|
|
|
InsertTailList (&mBdsHotKeyList, &HotKeyOption->Link);
|
|
HotKeyRegisterOptionHotKey (HotKeyOption);
|
|
|
|
*HotKeyHandle = (EFI_HANDLE *) (UINTN) HotKeyOption;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Register a load option to invoke when a specified hot key chord is detected.
|
|
|
|
@param[in] This A pointer to the current instance of this protocol.
|
|
@param[in] HotKey A pointer to a null-terminated ASCII string that specifies one or more hot key chords
|
|
for this hot key.
|
|
@param[in] HotKeyLoadOption A pointer to a load option that specifies the application to load when the hot key is
|
|
pressed, along with the load options to pass to that application.
|
|
@param[out] HotKeyHandle On return, a pointer to a handle that uniquely specifies this hot key.
|
|
|
|
@retval EFI_SUCCESS Hot key registered.
|
|
@retval EFI_INVALID_PARAMETER The hot key string was NULL, or had an invalid format.
|
|
The hot key attributes had bits set that are unsupported.
|
|
@retval EFI_ALREADY_STARTED A hot key with one of the specified hot key chords has already been registered.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BdsServicesRegisterLoadOptionHotKey (
|
|
IN H2O_BDS_SERVICES_PROTOCOL *This,
|
|
IN CONST CHAR8 *HotKey,
|
|
IN CONST H2O_BDS_LOAD_OPTION *HotKeyLoadOption,
|
|
OUT EFI_HANDLE *HotKeyHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index;
|
|
UINT32 ChordCount;
|
|
H2O_HOT_KEY_CHORD *ChordList;
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
|
|
if (!mHotKeySupport) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
if (This == NULL || HotKey == NULL || HotKeyLoadOption == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = GetChordList ((CHAR8 *) HotKey, &ChordList, &ChordCount);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
for (Index = 0; Index < ChordCount; Index++) {
|
|
if (IsChordRegistered (&ChordList[Index])) {
|
|
DEBUG((EFI_D_INFO, "BdsHotKey: Number %d of hot key (%a) is already registered before.\n", Index + 1, HotKey));
|
|
FreePool (ChordList);
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
}
|
|
|
|
HotKeyOption = AllocateZeroPool (sizeof(H2O_HOT_KEY_OPTION));
|
|
if (HotKeyOption == NULL) {
|
|
FreePool (ChordList);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
HotKeyOption->Signature = H2O_BDS_HOT_KEY_SIGNATURE;
|
|
HotKeyOption->Type = REGISTERED_LOAD_OPTION_HOT_KEY;
|
|
HotKeyOption->TypeValue.LoadOption = CopyLoadOption ((H2O_BDS_LOAD_OPTION *) HotKeyLoadOption);
|
|
HotKeyOption->HotKeyChordCount = ChordCount;
|
|
HotKeyOption->HotKeyChordList = ChordList;
|
|
CopyMem (&HotKeyOption->Description , &mDefaultHotKeyDescPosn , sizeof(H2O_HOT_KEY_DESCRIPTION));
|
|
CopyMem (&HotKeyOption->QuietDescription, &mDefaultHotKeyDescQuietPosn, sizeof(H2O_HOT_KEY_DESCRIPTION));
|
|
|
|
InsertTailList (&mBdsHotKeyList, &HotKeyOption->Link);
|
|
HotKeyRegisterOptionHotKey (HotKeyOption);
|
|
|
|
*HotKeyHandle = (EFI_HANDLE *) (UINTN) HotKeyOption;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Unregister a previously registered hot key chord.
|
|
|
|
@param[in] This A pointer to the current instance of this protocol.
|
|
@param[in] HotKeyHandle Handle that specifies a previously registered hot key.
|
|
|
|
@retval EFI_SUCCESS Function completed successfully.
|
|
@retval EFI_INVALID_PARAMETER Hot key handle is invalid.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BdsServicesUnregisterHotKey (
|
|
IN H2O_BDS_SERVICES_PROTOCOL *This,
|
|
IN EFI_HANDLE HotKeyHandle
|
|
)
|
|
{
|
|
H2O_HOT_KEY_OPTION *HotKeyOption;
|
|
|
|
if (This == NULL || HotKeyHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
HotKeyOption = GetHotKeyOption (HotKeyHandle);
|
|
if (HotKeyOption == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
switch (HotKeyOption->Type) {
|
|
|
|
case REGISTERED_CALLBACK_HOT_KEY:
|
|
break;
|
|
|
|
case REGISTERED_LOAD_OPTION_HOT_KEY:
|
|
if (HotKeyOption->TypeValue.LoadOption != NULL) {
|
|
gBdsServices->FreeLoadOption (gBdsServices, HotKeyOption->TypeValue.LoadOption);
|
|
}
|
|
break;
|
|
|
|
case UEFI_HOT_KEY:
|
|
case PCD_HOT_KEY:
|
|
default:
|
|
//
|
|
// UEFI hot key and PCD hot key should not be unregistered now, becuase caller should not get this Handle value.
|
|
//
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
RemoveEntryList (&HotKeyOption->Link);
|
|
|
|
FreeChordList (HotKeyOption->HotKeyChordList, HotKeyOption->HotKeyChordCount);
|
|
if (HotKeyOption->DevicePath != NULL) {
|
|
FreePool (HotKeyOption->DevicePath);
|
|
}
|
|
if (HotKeyOption->Options != NULL) {
|
|
FreePool (HotKeyOption->Options);
|
|
}
|
|
FreePool (HotKeyOption);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|