39. RegisterKeyNotify / UnrigisterKeyNotify 함수를 사용해 단축키 기능을 추가하는 드라이버 만들기
39장에서는 단축키 조합에 대한 callback을 등록하는 드라이버를 만든다
UEFI에서는 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL의 RegisterKeyNotify 함수를 사용할 수 있다.
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.RegisterKeyNotify()
Summary:
Register a notification function for a particular keystroke for the input device.
Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY) (
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
IN EFI_KEY_DATA *KeyData,
IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction,
OUT VOID **NotifyHandle
);
Parameters:
This A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance.
KeyData A pointer to a buffer that is filled in with the keystroke information
for the key that was pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState and
KeyData.KeyState.KeyShiftState are 0, then any incomplete keystroke will trigger a
notification of the KeyNotificationFunction.
KeyNotificationFunction Points to the function to be called when the key sequence is typed
specified by KeyData.
NotifyHandle Points to the unique handle assigned to the registered notification.
Description:
The RegisterKeystrokeNotify() function registers a function which will be called when a specified
keystroke will occur. The keystroke being specified can be for any combination of KeyData.Key or
KeyData.KeyState information.
callback 함수의 프로토타입은 다음과 같다.
typedefEFI_STATUS(EFIAPI *EFI_KEY_NOTIFY_FUNCTION) ( IN EFI_KEY_DATA *KeyData );
EFI_KEY_DATA의 경우, 두 개의 필드가 있는 구조이다.
typedefstruct { EFI_INPUT_KEY Key; // 입력 장치에서 반환된 EFI scan code 및 UNICODE 값이다. EFI_KEY_STATE KeyState; // input modifier 값 뿐만 아니라, 다양한 토글 속성의 현 상태이다.} EFI_KEY_DATA
EFI_KEY_STATE 유형이 새로 추가되었으며, SimpleTestInEx.h 파일에서 찾을 수 있다.
///
/// EFI_KEY_TOGGLE_STATE. The toggle states are defined.
/// They are: EFI_TOGGLE_STATE_VALID, EFI_SCROLL_LOCK_ACTIVE
/// EFI_NUM_LOCK_ACTIVE, EFI_CAPS_LOCK_ACTIVE
///
typedef UINT8 EFI_KEY_TOGGLE_STATE;
typedef struct _EFI_KEY_STATE {
///
/// Reflects the currently pressed shift
/// modifiers for the input device. The
/// returned value is valid only if the high
/// order bit has been set.
///
UINT32 KeyShiftState;
///
/// Reflects the current internal state of
/// various toggled attributes. The returned
/// value is valid only if the high order
/// bit has been set.
///
EFI_KEY_TOGGLE_STATE KeyToggleState;
} EFI_KEY_STATE;
KeyShiftState / KeyToggleState 필드에 코딩되어 있는 정보를 이해하려면, 동일한 파일에서 이 정의를 살펴보자.
//
// Any Shift or Toggle State that is valid should have
// high order bit set.
//
// Shift state
//
#define EFI_SHIFT_STATE_VALID 0x80000000
#define EFI_RIGHT_SHIFT_PRESSED 0x00000001
#define EFI_LEFT_SHIFT_PRESSED 0x00000002
#define EFI_RIGHT_CONTROL_PRESSED 0x00000004
#define EFI_LEFT_CONTROL_PRESSED 0x00000008
#define EFI_RIGHT_ALT_PRESSED 0x00000010
#define EFI_LEFT_ALT_PRESSED 0x00000020
#define EFI_RIGHT_LOGO_PRESSED 0x00000040
#define EFI_LEFT_LOGO_PRESSED 0x00000080
#define EFI_MENU_KEY_PRESSED 0x00000100
#define EFI_SYS_REQ_PRESSED 0x00000200
//
// Toggle state
//
#define EFI_TOGGLE_STATE_VALID 0x80
#define EFI_KEY_STATE_EXPOSED 0x40
#define EFI_SCROLL_LOCK_ACTIVE 0x01
#define EFI_NUM_LOCK_ACTIVE 0x02
#define EFI_CAPS_LOCK_ACTIVE 0x04
또한 이런 EDKII 파일(SimpleTextInEx.h)에는 스캔 코드(위, 아래, 오른쪽, 왼쪽, home, end, delete, f1-f24 등)에 대한 정의가 포함되어 있다. 따라서 단축키에 대한 스캔 코드를 사용하려는 경우, 해당 파일을 살펴보자.
마지막으로 드라이버에 대한 코드를 작성한다.
이 드라이버의 이름은 HotKeyDriver라고 짓는다. entry point 함수에 바로가기 키 조합을 등록하고 언로드 기능에서 등록 취소한다.
파일 시작 부분에서 NotifyHandle에 대한 전역 변수(드라이버 언로드함수에서 사용되므로)를추가하고, MyKeyNotificationFunction callback 함수를 정의한다.
EFI_HANDLE NotifyHandle;
EFI_STATUS EFIAPI MyKeyNotificationFunction(EFI_KEY_DATA* KeyData)
{
Print(L"\nHot Key 1 is pressed\n"); // we add '\n' in the begining, so the output wouldn't interfere with the UEFI shell prompt
return EFI_SUCCESS;
}
지금부턴 numlock이 활성화된 경우 z를 누를 때마다 실행이 되는 또 다른 callback을 작성해 보겠다.
EFI_HANDLE NotifyHandle1;
EFI_STATUS EFIAPI MyKeyNotificationFunction1(EFI_KEY_DATA* KeyData)
{
Print(L"\nHot Key 2 is pressed\n"); // we add '\n' in the begining, so the output wouldn't interfere with the UEFI shell prompt
return EFI_SUCCESS; // or another our callback function
}
드라이버 언로드 시, callback 함수의 등록을 취소해야 한다고 위에서 언급했다. 이에 대한 이유가 궁금할 것이다.
MyKeyNotificationFunction 및 MyKeyNotificationFunction1와 같은 함수는 HotKeyDriver 메모리에 정의되어 있기 때문이다. 메모리에서 HotKeyDriver를 언로드하면 이 메모리는 해제/비활성화 된다.
그리고 바로가기 키 조합에서 push 시스템은 포인터에서 유효하지 않은 영역에 대한 callback 함수를 역참조하고자 시도한다. 이 점이 예외로 이어진다.
등록 취소 실행의 경우, EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL은 UnreisterKeyNotify 함수를 정의한다.
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.UnregisterKeyNotify()
Summary:
Remove the notification that was previously registered.
Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY) (
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
IN VOID *NotificationHandle
);
Parameters:
This A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance.
NotificationHandle The handle of the notification function being unregistered.
Description:
The UnregisterKeystrokeNotify() function removes the notification which was previously registered.
callback 함수를 등록 취소하는 데 사용
EFI_STATUS
EFIAPI
HotKeyDriverUnload(
IN EFI_HANDLE ImageHandle
)
{
if (!InputEx)
return EFI_SUCCESS;
EFI_STATUS Status;
if (!NotifyHandle) {
Status = InputEx->UnregisterKeyNotify(InputEx,
(VOID*)NotifyHandle);
if (EFI_ERROR(Status)) {
Print(L"Error! Can't perform RegisterKeyNotify: %r", Status);
return Status;
}
}
if (!NotifyHandle1) {
Status = InputEx->UnregisterKeyNotify(InputEx,
(VOID*)NotifyHandle1);
if (EFI_ERROR(Status)) {
Print(L"Error! Can't perform RegisterKeyNotify: %r", Status);
return Status;
}
}
return EFI_SUCCESS;
}
이제 다 되었으니 드라이버를 빌드하고 테스트하자.
일반적이지 않은 키를 escape sequence로 터미널 변환하는 것을 방지하려면, -nograpic 옵션을 빼고 QEMU를 실행해야 한다.