alder_lake_bios/Insyde/InsydeModulePkg/Bus/I2c/I2cKbDxe/I2cKeyboard.c

1350 lines
38 KiB
C

/** @file
Helper functions for I2C Keyboard Driver
;******************************************************************************
;* Copyright (c) 2014 - 2019, 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 "Efikey.h"
#include "I2cKeyboard.h"
//
// I2C Key Code to Efi key mapping table
// Format:<efi scan code>, <unicode without shift>, <unicode with shift>
//
STATIC
UINT8 KeyConvertionTable[KEYCODE_MAX_MAKE][3] = {
SCAN_NULL, 'a', 'A', // 0x04
SCAN_NULL, 'b', 'B', // 0x05
SCAN_NULL, 'c', 'C', // 0x06
SCAN_NULL, 'd', 'D', // 0x07
SCAN_NULL, 'e', 'E', // 0x08
SCAN_NULL, 'f', 'F', // 0x09
SCAN_NULL, 'g', 'G', // 0x0A
SCAN_NULL, 'h', 'H', // 0x0B
SCAN_NULL, 'i', 'I', // 0x0C
SCAN_NULL, 'j', 'J', // 0x0D
SCAN_NULL, 'k', 'K', // 0x0E
SCAN_NULL, 'l', 'L', // 0x0F
SCAN_NULL, 'm', 'M', // 0x10
SCAN_NULL, 'n', 'N', // 0x11
SCAN_NULL, 'o', 'O', // 0x12
SCAN_NULL, 'p', 'P', // 0x13
SCAN_NULL, 'q', 'Q', // 0x14
SCAN_NULL, 'r', 'R', // 0x15
SCAN_NULL, 's', 'S', // 0x16
SCAN_NULL, 't', 'T', // 0x17
SCAN_NULL, 'u', 'U', // 0x18
SCAN_NULL, 'v', 'V', // 0x19
SCAN_NULL, 'w', 'W', // 0x1A
SCAN_NULL, 'x', 'X', // 0x1B
SCAN_NULL, 'y', 'Y', // 0x1C
SCAN_NULL, 'z', 'Z', // 0x1D
SCAN_NULL, '1', '!', // 0x1E
SCAN_NULL, '2', '@', // 0x1F
SCAN_NULL, '3', '#', // 0x20
SCAN_NULL, '4', '$', // 0x21
SCAN_NULL, '5', '%', // 0x22
SCAN_NULL, '6', '^', // 0x23
SCAN_NULL, '7', '&', // 0x24
SCAN_NULL, '8', '*', // 0x25
SCAN_NULL, '9', '(', // 0x26
SCAN_NULL, '0', ')', // 0x27
SCAN_NULL, 0x0d, 0x0d, // 0x28 Enter
SCAN_ESC, 0x00, 0x00, // 0x29 Esc
SCAN_NULL, 0x08, 0x08, // 0x2A Backspace
SCAN_NULL, 0x09, 0x09, // 0x2B Tab
SCAN_NULL, ' ', ' ', // 0x2C Spacebar
SCAN_NULL, '-', '_', // 0x2D
SCAN_NULL, '=', '+', // 0x2E
SCAN_NULL, '[', '{', // 0x2F
SCAN_NULL, ']', '}', // 0x30
SCAN_NULL, '\\', '|', // 0x31
SCAN_NULL, '\\', '|', // 0x32 Keyboard US \ and |
SCAN_NULL, ';', ':', // 0x33
SCAN_NULL, '\'', '"', // 0x34
SCAN_NULL, '`', '~', // 0x35 Keyboard Grave Accent and Tlide
SCAN_NULL, ',', '<', // 0x36
SCAN_NULL, '.', '>', // 0x37
SCAN_NULL, '/', '?', // 0x38
SCAN_NULL, 0x00, 0x00, // 0x39 CapsLock
SCAN_F1, 0x00, 0x00, // 0x3A
SCAN_F2, 0x00, 0x00, // 0x3B
SCAN_F3, 0x00, 0x00, // 0x3C
SCAN_F4, 0x00, 0x00, // 0x3D
SCAN_F5, 0x00, 0x00, // 0x3E
SCAN_F6, 0x00, 0x00, // 0x3F
SCAN_F7, 0x00, 0x00, // 0x40
SCAN_F8, 0x00, 0x00, // 0x41
SCAN_F9, 0x00, 0x00, // 0x42
SCAN_F10, 0x00, 0x00, // 0x43
SCAN_F11, 0x00, 0x00, // 0x44 F11
SCAN_F12, 0x00, 0x00, // 0x45 F12
SCAN_NULL, 0x00, 0x00, // 0x46 PrintScreen
SCAN_NULL, 0x00, 0x00, // 0x47 Scroll Lock
SCAN_PAUSE, 0x00, 0x00, // 0x48 Pause
SCAN_INSERT, 0x00, 0x00, // 0x49
SCAN_HOME, 0x00, 0x00, // 0x4A
SCAN_PAGE_UP, 0x00, 0x00, // 0x4B
SCAN_DELETE, 0x00, 0x00, // 0x4C
SCAN_END, 0x00, 0x00, // 0x4D
SCAN_PAGE_DOWN, 0x00, 0x00, // 0x4E
SCAN_RIGHT, 0x00, 0x00, // 0x4F
SCAN_LEFT, 0x00, 0x00, // 0x50
SCAN_DOWN, 0x00, 0x00, // 0x51
SCAN_UP, 0x00, 0x00, // 0x52
SCAN_NULL, 0x00, 0x00, // 0x53 NumLock
SCAN_NULL, '/', '/', // 0x54
SCAN_NULL, '*', '*', // 0x55
SCAN_NULL, '-', '-', // 0x56
SCAN_NULL, '+', '+', // 0x57
SCAN_NULL, 0x0d, 0x0d, // 0x58
SCAN_END, '1', '1', // 0x59
SCAN_DOWN, '2', '2', // 0x5A
SCAN_PAGE_DOWN, '3', '3', // 0x5B
SCAN_LEFT, '4', '4', // 0x5C
SCAN_NULL, '5', '5', // 0x5D
SCAN_RIGHT, '6', '6', // 0x5E
SCAN_HOME, '7', '7', // 0x5F
SCAN_UP, '8', '8', // 0x60
SCAN_PAGE_UP, '9', '9', // 0x61
SCAN_INSERT, '0', '0', // 0x62
SCAN_DELETE, '.', '.', // 0x63
SCAN_NULL, '\\', '|', // 0x64 Keyboard Non-US \ and |
SCAN_NULL, 0x00, 0x00, // 0x65 Keyboard Application
SCAN_NULL, 0x00, 0x00, // 0x66 Keyboard Power
SCAN_NULL, '=' , '=' , // 0x67 Keypad =
SCAN_F13, 0x00, 0x00, // 0x68
SCAN_F14, 0x00, 0x00, // 0x69
SCAN_F15, 0x00, 0x00, // 0x6A
SCAN_F16, 0x00, 0x00, // 0x6B
SCAN_F17, 0x00, 0x00, // 0x6C
SCAN_F18, 0x00, 0x00, // 0x6D
SCAN_F19, 0x00, 0x00, // 0x6E
SCAN_F20, 0x00, 0x00, // 0x6F
SCAN_F21, 0x00, 0x00, // 0x70
SCAN_F22, 0x00, 0x00, // 0x71
SCAN_F23, 0x00, 0x00, // 0x72
SCAN_F24, 0x00, 0x00, // 0x73
SCAN_MUTE, 0x00, 0x00, // 0x7F
SCAN_VOLUME_UP, 0x00, 0x00, // 0x80
SCAN_VOLUME_DOWN, 0x00, 0x00 // 0x81
};
STATIC KB_MODIFIER KB_Mod[8] = {
{ MOD_CONTROL_L, 0xe0 }, // 11100000
{ MOD_CONTROL_R, 0xe4 }, // 11100100
{ MOD_SHIFT_L, 0xe1 }, // 11100001
{ MOD_SHIFT_R, 0xe5 }, // 11100101
{ MOD_ALT_L, 0xe2 }, // 11100010
{ MOD_ALT_R, 0xe6 }, // 11100110
{ MOD_WIN_L, 0xe3 }, // 11100011
{ MOD_WIN_R, 0xe7 }, // 11100111
};
STATIC
UINT8 KbcCodeTable[][4] = {
// Original Shift Alt Ctrl
0x1E, 0x1E, 0x1E, 0x1E, // 0x04 'A'
0x30, 0x30, 0x30, 0x30, // 0x05 'B'
0x2E, 0x2E, 0x2E, 0x2E, // 0x06 'C'
0x20, 0x20, 0x20, 0x20, // 0x07 'D'
0x12, 0x12, 0x12, 0x12, // 0x08 'E'
0x21, 0x21, 0x21, 0x21, // 0x09 'F'
0x22, 0x22, 0x22, 0x22, // 0x0A 'G'
0x23, 0x23, 0x23, 0x23, // 0x0B 'H'
0x17, 0x17, 0x17, 0x17, // 0x0C 'I'
0x24, 0x24, 0x24, 0x24, // 0x0D 'J'
0x25, 0x25, 0x25, 0x25, // 0x0E 'K'
0x26, 0x26, 0x26, 0x26, // 0x0F 'L'
0x32, 0x32, 0x32, 0x32, // 0x10 'M'
0x31, 0x31, 0x31, 0x31, // 0x11 'N'
0x18, 0x18, 0x18, 0x18, // 0x12 'O'
0x19, 0x19, 0x19, 0x19, // 0x13 'P'
0x10, 0x10, 0x10, 0x10, // 0x14 'Q'
0x13, 0x13, 0x13, 0x13, // 0x15 'R'
0x1F, 0x1F, 0x1F, 0x1F, // 0x16 'S'
0x14, 0x14, 0x14, 0x14, // 0x17 'T'
0x16, 0x16, 0x16, 0x16, // 0x18 'U'
0x2F, 0x2F, 0x2F, 0x2F, // 0x19 'V'
0x11, 0x11, 0x11, 0x11, // 0x1A 'W'
0x2D, 0x2D, 0x2D, 0x2D, // 0x1B 'X'
0x15, 0x15, 0x15, 0x15, // 0x1C 'Y'
0x2C, 0x2C, 0x2C, 0x2C, // 0x1D 'Z'
0x02, 0x02, 0x78, 0x00, // 0x1E '1 !'
0x03, 0x03, 0x79, 0x03, // 0x1F '2 @'
0x04, 0x04, 0x7A, 0x00, // 0x20 '3 #'
0x05, 0x05, 0x7B, 0x00, // 0x21 '4 $'
0x06, 0x06, 0x7C, 0x00, // 0x22 '5 %'
0x07, 0x07, 0x7D, 0x07, // 0x23 '6 ^'
0x08, 0x08, 0x7E, 0x00, // 0x24 '7 &'
0x09, 0x09, 0x7F, 0x00, // 0x25 '8 *'
0x0A, 0x0A, 0x81, 0x00, // 0x26 '9 ('
0x0B, 0x0B, 0x82, 0x00, // 0x27 '0 )'
0x1C, 0x1C, 0x1C, 0x1C, // 0x28 'Enter'
0x01, 0x01, 0x01, 0x01, // 0x29 'Esc'
0x0E, 0x0E, 0x0E, 0x0E, // 0x2A 'Backspace'
0x0F, 0x0F, 0xA5, 0x94, // 0x2B 'Tab'
0x39, 0x39, 0x39, 0x39, // 0x2C 'Space bar'
0x0C, 0x0C, 0x82, 0x0C, // 0x2D '- _'
0x0D, 0x0D, 0x83, 0x00, // 0x2E '= +'
0x1A, 0x1A, 0x1A, 0x1A, // 0x2F '[ {'
0x1B, 0x1B, 0x1B, 0x1B, // 0x30 '] }'
0x2B, 0x2B, 0x2B, 0x2B, // 0x31 '\ |'
0x2B, 0x2B, 0x2B, 0x2B, // 0x32 Non-US '# ~' ******
0x27, 0x27, 0x27, 0x00, // 0x33 '; :'
0x28, 0x28, 0x28, 0x00, // 0x34 '' "'
0x29, 0x29, 0x29, 0x00, // 0x35 ;` ~'
0x33, 0x33, 0x33, 0x00, // 0x36 ', <'
0x34, 0x34, 0x34, 0x00, // 0x37 '. >'
0x35, 0x35, 0x35, 0x00, // 0x38 '/ ?'
0x3A, 0x3A, 0x3A, 0x3A, // 0x39 'CapsLock'
0x3B, 0x54, 0x68, 0x5E, // 0x3A 'F1'
0x3C, 0x55, 0x69, 0x5F, // 0x3B 'F2'
0x3D, 0x56, 0x6A, 0x60, // 0x3C 'F3'
0x3E, 0x57, 0x6B, 0x61, // 0x3D 'F4'
0x3F, 0x58, 0x6C, 0x62, // 0x3E 'F5'
0x40, 0x59, 0x6D, 0x63, // 0x3F 'F6'
0x41, 0x5A, 0x6E, 0x64, // 0x40 'F7'
0x42, 0x5B, 0x6F, 0x65, // 0x41 'F8'
0x43, 0x5C, 0x70, 0x66, // 0x42 'F9'
0x44, 0x5D, 0x71, 0x67, // 0x43 'F10'
0x57, 0x87, 0x8B, 0x89, // 0x44 'F11'
0x58, 0x88, 0x8C, 0x8A, // 0x45 'F12'
0x80, 0x80, 0x80, 0x72, // 0x46 'PrntScrn' E0,2A,E0,37 E0,B7,E0,AA
0x46, 0x46, 0x46, 0x46, // 0x47 'ScrollLock'
0x81, 0x81, 0x81, 0x81, // 0x48 'Pause' E1,1D,45 E1,9D,C5
0xD2, 0xD2, 0xA2, 0x92, // 0x49 'Insert' E0,52 E0,D2
0xC7, 0xC7, 0x97, 0x77, // 0x4A 'Home' E0,47 E0,C7
0xC9, 0xC9, 0x99, 0x84, // 0x4B 'PageUp' E0,49 E0,C9
0xD3, 0xD3, 0xA3, 0x93, // 0x4C 'Delete Forward' E0,53 E0,D3
0xCF, 0xCF, 0x9F, 0x75, // 0x4D 'End' E0,4F E0,CF
0xD1, 0xD1, 0xA1, 0x76, // 0x4E 'PageDown' E0,51 E0,D1
0xCD, 0xCD, 0x9D, 0x74, // 0x4F 'Right Arrow' E0,4D E0,CD
0xCB, 0xCB, 0x9B, 0x73, // 0x50 'Left Arrow' E0,4B E0,CB
0xD0, 0xD0, 0xA0, 0x91, // 0x51 'Down Arrow' E0,50 E0,D0
0xC8, 0xC8, 0x98, 0x8D, // 0x52 'Up Arrow' E0,48 E0,C8
0x45, 0x45, 0x45, 0x45, // 0x53 'NumLock'
0xB5, 0xB5, 0xA4, 0x95, // 0x54 'keypad /' E0,35 E0,B5
0x37, 0x37, 0x37, 0x96, // 0x55 'keypad *'
0x4A, 0x4A, 0x4A, 0x8E, // 0x56 'keypad -'
0x4E, 0x4E, 0x4E, 0x90, // 0x57 'keypad +'
0x9C, 0x9C, 0xA6, 0x9C, // 0x58 'keypad Enter' E0,1C E0,9C
0x4F, 0x4F, 0x9F, 0x75, // 0x59 'keypad 1 End'
0x50, 0x50, 0xA0, 0x91, // 0x5A 'keypad 2 DownArrow'
0x51, 0x51, 0xA1, 0x76, // 0x5B 'keypad 3 PageDn'
0x4B, 0x4B, 0x9B, 0x73, // 0x5C 'keypad 4 LeftArrow'
0x4C, 0x4C, 0x00, 0x8F, // 0x5D 'keypad 5'
0x4D, 0x4D, 0x9D, 0x74, // 0x5E 'keypad 6 RightArrow'
0x47, 0x47, 0x97, 0x77, // 0x5F 'keypad 7 Home'
0x48, 0x48, 0x98, 0x8D, // 0x60 'keypad 8 UpArrow'
0x49, 0x49, 0x99, 0x84, // 0x61 'keypad 9 PageUp'
0x52, 0x52, 0xA2, 0x92, // 0x62 'keypad 0 Insert'
0x53, 0x53, 0xA3, 0x93, // 0x63 'keypad . Del'
0x56, 0x56, 0x56, 0x56, // 0x64 '\ |'
0xDD, 0xDD, 0xDD, 0xDD // 0x65 'Application' E0,5D E0,DD
};
STATIC
UINT8 CtrlIgnoreKey[] = {
0x1E, // '1','!'
0x20, // '3','#'
0x21, // '4','$'
0x22, // '5','%'
0x24, // '7','&'
0x25, // '8','*'
0x26, // '9','('
0x27, // '0',')'
0x2E, // '=','+'
0x33, // ';',':'
0x34, // '\'','"'
0x35, // '`','~'
0x36, // ',','<'
0x37, // '.','>'
0x38 // '/','?'
};
/**
Handler function for I2C Keyboard's interrupt transfer.
@param Data A pointer to a buffer that is filled with key data which is
retrieved via asynchronous interrupt transfer.
@param DataLength Indicates the size of the data buffer.
@param Context Pointing to I2C_KB_DEV instance.
@param Result Indicates the result of the asynchronous interrupt transfer.
@retval EFI_SUCCESS Success
@retval EFI_DEVICE_ERROR Hardware Error
**/
EFI_STATUS
EFIAPI
KeyboardHandler (
IN VOID *Data,
IN UINTN DataLength,
IN VOID *Context,
IN UINT32 Result
)
{
I2C_KB_DEV *KbDev;
UINT8 *CurKeyCodeBuffer;
UINT8 *OldKeyCodeBuffer;
UINT8 CurModifierMap;
UINT8 OldModifierMap;
UINTN Index;
UINT8 Index2;
BOOLEAN Down;
EFI_STATUS Status;
BOOLEAN KeyRelease;
BOOLEAN KeyPress;
UINTN SavedTail;
KEY Key;
UINT8 NewRepeatKey;
if (DataLength == 0 || Data == NULL) {
return EFI_SUCCESS;
}
KbDev = (I2C_KB_DEV *) Context;
NewRepeatKey = 0;
CurKeyCodeBuffer = (UINT8 *) Data;
OldKeyCodeBuffer = KbDev->LastKeyCodeArray;
//
// checks for new key stroke.
// if no new key got, return immediately.
//
for (Index = 0; Index < 8; Index++) {
if (OldKeyCodeBuffer[Index] != CurKeyCodeBuffer[Index]) {
break;
}
}
if (Index == 8) {
return EFI_SUCCESS;
}
//
// Parse the modifier key
//
CurModifierMap = CurKeyCodeBuffer[0];
OldModifierMap = OldKeyCodeBuffer[0];
//
// handle modifier key's pressing or releasing situation.
//
for (Index = 0; Index < 8; Index++) {
if ((CurModifierMap & KB_Mod[Index].Mask) != (OldModifierMap & KB_Mod[Index].Mask)) {
//
// if current modifier key is up, then
// CurModifierMap & KB_Mod[Index].Mask = 0;
// otherwize it is a non-zero value.
// Inserts the pressed modifier key into key buffer.
//
Down = (UINT8) (CurModifierMap & KB_Mod[Index].Mask);
InsertKeyCode (KbDev, KB_Mod[Index].Key, Down);
}
}
//
// handle normal key's releasing situation
//
KeyRelease = FALSE;
for (Index = 2; Index < 8; Index++) {
if (!KBD_VALID_KEYCODE (OldKeyCodeBuffer[Index])) {
continue;
}
KeyRelease = TRUE;
for (Index2 = 2; Index2 < 8; Index2++) {
if (!KBD_VALID_KEYCODE (CurKeyCodeBuffer[Index2])) {
continue;
}
if (OldKeyCodeBuffer[Index] == CurKeyCodeBuffer[Index2]) {
KeyRelease = FALSE;
break;
}
}
if (KeyRelease) {
InsertKeyCode (
KbDev,
OldKeyCodeBuffer[Index],
0
);
//
// the original reapeat key is released.
//
if (OldKeyCodeBuffer[Index] == KbDev->RepeatKey) {
KbDev->RepeatKey = 0;
}
}
}
//
// original repeat key is released, cancel the repeat timer
//
if (KbDev->RepeatKey == 0 && KbDev->RepeatPollingHandle != NULL) {
gBS->CloseEvent (KbDev->RepeatPollingHandle);
KbDev->RepeatPollingHandle = NULL;
}
//
// handle normal key's pressing situation
//
KeyPress = FALSE;
for (Index = 2; Index < 8; Index++) {
if (!KBD_VALID_KEYCODE (CurKeyCodeBuffer[Index])) {
continue;
}
KeyPress = TRUE;
for (Index2 = 2; Index2 < 8; Index2++) {
if (!KBD_VALID_KEYCODE (OldKeyCodeBuffer[Index2])) {
continue;
}
if (CurKeyCodeBuffer[Index] == OldKeyCodeBuffer[Index2]) {
KeyPress = FALSE;
break;
}
}
if (KeyPress) {
InsertKeyCode (KbDev, CurKeyCodeBuffer[Index], 1);
//
// NumLock/CapsLock/ScrollLock pressed
//
if (CurKeyCodeBuffer[Index] != 0x53 && CurKeyCodeBuffer[Index] != 0x39 && CurKeyCodeBuffer[Index] != 0x47) {
NewRepeatKey = CurKeyCodeBuffer[Index];
}
//
// do not repeat the original repeated key
//
KbDev->RepeatKey = 0;
}
}
//
// Update LastKeycodeArray[] buffer in the
// Usb Keyboard Device data structure.
//
for (Index = 0; Index < 8; Index++) {
KbDev->LastKeyCodeArray[Index] = CurKeyCodeBuffer[Index];
}
//
// pre-process I2cKeyQueue, pop out the ctrl,alt,del key in sequence
// and judge whether it will invoke reset event.
//
SavedTail = KbDev->I2cKeyQueue.Tail;
Index = KbDev->I2cKeyQueue.Head;
while (Index != SavedTail) {
Dequeue (&KbDev->I2cKeyQueue, &Key, sizeof (Key));
switch (Key.KeyCode) {
case 0xe1:
case 0xe5:
if (Key.Down) {
KbDev->ShiftOn = 1;
} else {
KbDev->ShiftOn = 0;
}
break;
case 0xe0:
case 0xe4:
if (Key.Down) {
KbDev->CtrlOn = 1;
} else {
KbDev->CtrlOn = 0;
}
break;
case 0xe2:
case 0xe6:
if (Key.Down) {
KbDev->AltOn = 1;
} else {
KbDev->AltOn = 0;
}
break;
//
// Del Key Code
//
case 0x4c:
case 0x63:
if (Key.Down) {
if (KbDev->CtrlOn && KbDev->AltOn) {
gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, KEYBOARD_RESET_SIGNATURE);
}
}
break;
default:
break;
}
//
// insert the key back to the buffer.
// so the key sequence will not be destroyed.
//
InsertKeyCode (
KbDev,
Key.KeyCode,
Key.Down
);
Index = KbDev->I2cKeyQueue.Head;
}
//
// If have new key pressed, update the RepeatKey value, and set the
// timer to repeate delay timer
//
if (NewRepeatKey != 0) {
if (KbDev->RepeatPollingHandle) {
gBS->CloseEvent (KbDev->RepeatPollingHandle);
KbDev->RepeatPollingHandle = NULL;
}
//
// sets trigger time to "Repeat Delay Time",
// to trigger the repeat timer when the key is hold long
// enough time.
//
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
KeyboardRepeatHandler,
KbDev,
&KbDev->RepeatPollingHandle
);
if (EFI_ERROR(Status)) {
return EFI_OUT_OF_RESOURCES;
}
Status = gBS->SetTimer (
KbDev->RepeatPollingHandle,
TimerRelative,
KBD_REPEAT_DELAY
);
if (EFI_ERROR(Status)) {
return EFI_OUT_OF_RESOURCES;
}
KbDev->RepeatKey = NewRepeatKey;
}
return EFI_SUCCESS;
}
/**
Convert key code to KBC scan code
@param KbDev The I2C_KB_DEV instance.
@param Key Key code
@retval KBC scan code
**/
STATIC
UINT16
ConvertKbcScanCode (
IN I2C_KB_DEV *KbDev,
IN UINT8 Key
)
{
UINT16 KbcScanCode;
if (Key < 4 || Key >= (sizeof (KbcCodeTable) / 4) + 4) return 0;
if (KbDev->AltOn) {
KbcScanCode = KbcCodeTable[Key - 4][2];
} else if (KbDev->CtrlOn) {
KbcScanCode = KbcCodeTable[Key - 4][3];
} else if (KbDev->ShiftOn) {
KbcScanCode = KbcCodeTable[Key - 4][1];
} else {
KbcScanCode = KbcCodeTable[Key - 4][0];
if (KbcScanCode == 0x57 || KbcScanCode == 0x58) {
//
// Convert F11/F12 to KBC converted code
//
KbcScanCode = 0x85 + (KbcScanCode - 0x57);
} else if (KbcScanCode >= 0x82) {
//
// Double code key
//
KbcScanCode &= ~0x80;
KbcScanCode <<= 8;
KbcScanCode |= 0xE0;
}
}
return KbcScanCode;
}
/**
Retrieves a key character after parsing the raw data in keyboard buffer.
@param KbDev The I2C_KB_DEV instance.
@param KeyChar Points to the Key character after key parsing.
@retval EFI_SUCCESS Success
@retval EFI_NOT_READY Device is not ready
**/
EFI_STATUS
ParseKey (
IN I2C_KB_DEV *KbDev,
OUT UINT8 *KeyChar
)
{
KEY Key;
*KeyChar = 0;
while (!IsQueueEmpty (&KbDev->I2cKeyQueue)) {
//
// pops one raw data off.
//
Dequeue (&KbDev->I2cKeyQueue, &Key, sizeof (Key));
if (!Key.Down) {
switch (Key.KeyCode) {
case 0xe0:
KbDev->LeftCtrlOn = 0;
KbDev->CtrlOn = 0;
break;
case 0xe4:
KbDev->RightCtrlOn = 0;
KbDev->CtrlOn = 0;
break;
case 0xe1:
KbDev->LeftShiftOn = 0;
KbDev->ShiftOn = 0;
break;
case 0xe5:
KbDev->RightShiftOn = 0;
KbDev->ShiftOn = 0;
break;
case 0xe2:
KbDev->LeftAltOn = 0;
KbDev->AltOn = 0;
break;
case 0xe6:
KbDev->RightAltOn = 0;
KbDev->AltOn = 0;
break;
//
// Logo release
//
case 0xe3:
KbDev->LeftLogoOn = 0;
break;
case 0xe7:
KbDev->RightLogoOn = 0;
break;
//
// Menu key (App/Apps) release
//
case 0x65:
KbDev->MenuKeyOn = 0;
break;
//
// SysReq release
//
case 0x46:
KbDev->SysReqOn = 0;
break;
default:
break;
}
continue;
}
//
// Analyzes key pressing situation
//
switch (Key.KeyCode) {
case 0xe0:
KbDev->LeftCtrlOn = 1;
KbDev->CtrlOn = 1;
break;
case 0xe4:
KbDev->RightCtrlOn = 1;
KbDev->CtrlOn = 1;
break;
case 0xe1:
KbDev->LeftShiftOn = 1;
KbDev->ShiftOn = 1;
break;
case 0xe5:
KbDev->RightShiftOn = 1;
KbDev->ShiftOn = 1;
break;
case 0xe2:
KbDev->LeftAltOn = 1;
KbDev->AltOn = 1;
break;
case 0xe6:
KbDev->RightAltOn = 1;
KbDev->AltOn = 1;
break;
case 0xe3:
KbDev->LeftLogoOn = 1;
break;
case 0xe7:
KbDev->RightLogoOn = 1;
break;
case 0x53:
if (KbDev->IsCsmEnabled) {
if (!PS2_LED_UPDATING) {
USB_LED_UPDATE;
KbDev->NumLockOn ^= 1;
SyncKbdLed (KbDev);
USB_LED_UPDATED;
}
} else {
KbDev->NumLockOn ^= 1;
SyncKbdLed (KbDev);
}
break;
case 0x39:
if (KbDev->IsCsmEnabled) {
if (!PS2_LED_UPDATING) {
USB_LED_UPDATE;
KbDev->CapsOn ^= 1;
SyncKbdLed (KbDev);
USB_LED_UPDATED;
}
} else {
KbDev->CapsOn ^= 1;
SyncKbdLed (KbDev);
}
break;
case 0x47:
if (KbDev->IsCsmEnabled) {
if (!PS2_LED_UPDATING) {
USB_LED_UPDATE;
KbDev->ScrollOn ^= 1;
SyncKbdLed (KbDev);
USB_LED_UPDATED;
}
} else {
KbDev->ScrollOn ^= 1;
SyncKbdLed (KbDev);
}
break;
//
// PrintScreen,Pause,Application,Power
// keys are not valid EFI key
//
case 0x46:
KbDev->SysReqOn = 1;
break;
case 0x65:
KbDev->MenuKeyOn = 1;
break;
case 0x48:
break;
default:
break;
}
//
// When encountered Del Key...
//
if (Key.KeyCode == 0x4c || Key.KeyCode == 0x63) {
if (KbDev->CtrlOn && KbDev->AltOn) {
gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, KEYBOARD_RESET_SIGNATURE);
}
}
*KeyChar = Key.KeyCode;
if ((KbDev->IsCsmEnabled) && (Key.Down) && (Key.KeyCode < 0xe0) && (Key.KeyCode != 0x46) && (Key.KeyCode != 0x48)) {
//
// Put the KBC scan code into EBDA+0x164. The PrntScrn and Pause will be filter out to sync with PS2 path
//
*(UINT16*)&(EBDA(EBDA_KEYBORD_SCAN_CODE)) = ConvertKbcScanCode (KbDev, Key.KeyCode);
}
return EFI_SUCCESS;
}
return EFI_NOT_READY;
}
/**
Initialize the key state.
@param UsbKeyboardDevice The USB_KB_DEV instance.
@param KeyState A pointer to receive the key state information.
**/
VOID
InitializeKeyState (
IN I2C_KB_DEV *KbDev,
OUT EFI_KEY_STATE *KeyState
)
{
KeyState->KeyShiftState = EFI_SHIFT_STATE_VALID;
KeyState->KeyToggleState = EFI_TOGGLE_STATE_VALID;
if (KbDev->LeftCtrlOn) {
KeyState->KeyShiftState |= EFI_LEFT_CONTROL_PRESSED;
}
if (KbDev->RightCtrlOn) {
KeyState->KeyShiftState |= EFI_RIGHT_CONTROL_PRESSED;
}
if (KbDev->LeftAltOn) {
KeyState->KeyShiftState |= EFI_LEFT_ALT_PRESSED;
}
if (KbDev->RightAltOn) {
KeyState->KeyShiftState |= EFI_RIGHT_ALT_PRESSED;
}
if (KbDev->LeftShiftOn) {
KeyState->KeyShiftState |= EFI_LEFT_SHIFT_PRESSED;
}
if (KbDev->RightShiftOn) {
KeyState->KeyShiftState |= EFI_RIGHT_SHIFT_PRESSED;
}
if (KbDev->LeftLogoOn) {
KeyState->KeyShiftState |= EFI_LEFT_LOGO_PRESSED;
}
if (KbDev->RightLogoOn) {
KeyState->KeyShiftState |= EFI_RIGHT_LOGO_PRESSED;
}
if (KbDev->MenuKeyOn) {
KeyState->KeyShiftState |= EFI_MENU_KEY_PRESSED;
}
if (KbDev->SysReqOn) {
KeyState->KeyShiftState |= EFI_SYS_REQ_PRESSED;
}
if (KbDev->ScrollOn) {
KeyState->KeyToggleState |= EFI_SCROLL_LOCK_ACTIVE;
}
if (KbDev->NumLockOn) {
KeyState->KeyToggleState |= EFI_NUM_LOCK_ACTIVE;
}
if (KbDev->CapsOn) {
KeyState->KeyToggleState |= EFI_CAPS_LOCK_ACTIVE;
}
if (KbDev->IsSupportPartialKey) {
KeyState->KeyToggleState |= EFI_KEY_STATE_EXPOSED;
}
}
/**
Converts Keyboard code to EFI Scan Code.
@param KbDev The I2C_KB_DEV instance.
@param KeyChar Indicates the key code that will be interpreted.
@param Key A pointer to a buffer that is filled in with
the keystroke information for the key that
was pressed.
@retval EFI_NOT_READY Device is not ready
@retval EFI_SUCCESS Success
**/
EFI_STATUS
KeyCodeToEFIScanCode (
IN I2C_KB_DEV *KbDev,
IN UINT8 KeyChar,
OUT EFI_INPUT_KEY *Key
)
{
UINT8 Index;
if (!KBD_VALID_KEYCODE (KeyChar)) {
return EFI_NOT_READY;
}
if ((KeyChar >= 0xe0) && (KeyChar <= 0xe7)) {
//
// For Partial Keystroke support
//
Key->ScanCode = SCAN_NULL;
Key->UnicodeChar = CHAR_NULL;
} else {
//
// valid Key Code starts from 4
//
Index = (UINT8) (KeyChar - 4);
if (Index >= KEYCODE_MAX_MAKE) {
return EFI_NOT_READY;
}
//
// Undefined entries from 0x74 to 0x7E
//
if (KeyChar > KEYCODE_MAX_MAKE) {
Index = Index - 11;
}
Key->ScanCode = KeyConvertionTable[Index][0];
if (KbDev->ShiftOn) {
Key->UnicodeChar = KeyConvertionTable[Index][2];
//
// Need not return associated shift state if a class of printable characters that
// are normally adjusted by shift modifiers. e.g. Shift Key + 'f' key = 'F'
//
if ((KeyConvertionTable[Index][1] != CHAR_NULL) && (KeyConvertionTable[Index][2] != CHAR_NULL) &&
(KeyConvertionTable[Index][1] != KeyConvertionTable[Index][2])) {
KbDev->LeftShiftOn = 0;
KbDev->RightShiftOn = 0;
}
} else {
Key->UnicodeChar = KeyConvertionTable[Index][1];
}
if (KbDev->CapsOn) {
if (Key->UnicodeChar >= 'a' && Key->UnicodeChar <= 'z') {
Key->UnicodeChar = KeyConvertionTable[Index][2];
} else if (Key->UnicodeChar >= 'A' && Key->UnicodeChar <= 'Z') {
Key->UnicodeChar = KeyConvertionTable[Index][1];
}
}
//
// Translate the CTRL-Alpha characters to their corresponding control value
// (ctrl-a = 0x0001 through ctrl-Z = 0x001A, ctrl-[, ctrl-\, ctrl-], ctrl-^ and ctrl-_)
//
if (KbDev->CtrlOn) {
for (Index = 0; Index < sizeof (CtrlIgnoreKey); Index++) {
if (KeyChar == CtrlIgnoreKey[Index]) {
return EFI_NOT_READY;
}
}
}
if (KeyChar >= 0x59 && KeyChar <= 0x63) {
if (KbDev->NumLockOn && !KbDev->ShiftOn) {
Key->ScanCode = SCAN_NULL;
} else {
Key->UnicodeChar = 0x00;
}
}
}
if (Key->UnicodeChar == 0 && Key->ScanCode == SCAN_NULL) {
if (!KbDev->IsSupportPartialKey) {
return EFI_NOT_READY;
}
}
//
// Save Shift/Toggle state
//
InitializeKeyState (KbDev, &KbDev->KeyState);
return EFI_SUCCESS;
}
/**
Create the queue.
@param Queue Points to the queue.
@param ItemSize Size of the single item.
**/
VOID
InitQueue (
IN OUT I2C_SIMPLE_QUEUE *Queue,
IN UINTN ItemSize
)
{
UINTN Index;
Queue->ItemSize = ItemSize;
Queue->Head = 0;
Queue->Tail = 0;
if (Queue->Buffer[0] != NULL) {
FreePool (Queue->Buffer[0]);
}
Queue->Buffer[0] = AllocatePool (sizeof (Queue->Buffer) / sizeof (Queue->Buffer[0]) * ItemSize);
ASSERT (Queue->Buffer[0] != NULL);
for (Index = 1; Index < sizeof (Queue->Buffer) / sizeof (Queue->Buffer[0]); Index++) {
Queue->Buffer[Index] = ((UINT8 *) Queue->Buffer[Index - 1]) + ItemSize;
}
}
/**
Destroy the queue
@param Queue Points to the queue.
**/
VOID
DestroyQueue (
IN OUT I2C_SIMPLE_QUEUE *Queue
)
{
if (Queue->Buffer[0] != NULL) {
FreePool (Queue->Buffer[0]);
Queue->Buffer[0] = NULL;
}
}
/**
Check whether the queue is empty.
@param Queue Points to the queue.
@retval TRUE Queue is empty.
@retval FALSE Queue is not empty.
**/
BOOLEAN
IsQueueEmpty (
IN I2C_SIMPLE_QUEUE *Queue
)
{
//
// Meet FIFO empty condition
//
return (BOOLEAN) (Queue->Head == Queue->Tail);
}
/**
Check whether the queue is full.
@param Queue Points to the queue.
@retval TRUE Queue is full.
@retval FALSE Queue is not full.
**/
BOOLEAN
IsQueueFull (
IN I2C_SIMPLE_QUEUE *Queue
)
{
return (BOOLEAN) (((Queue->Tail + 1) % (MAX_KEY_ALLOWED + 1)) == Queue->Head);
}
/**
Enqueue the item to the queue.
@param Queue Points to the queue.
@param Item Points to the item to be enqueued.
@param ItemSize Size of the item.
**/
VOID
Enqueue (
IN OUT I2C_SIMPLE_QUEUE *Queue,
IN VOID *Item,
IN UINTN ItemSize
)
{
ASSERT (ItemSize == Queue->ItemSize);
//
// If keyboard buffer is full, throw the
// first key out of the keyboard buffer.
//
if (IsQueueFull (Queue)) {
Queue->Head = (Queue->Head + 1) % (MAX_KEY_ALLOWED + 1);
}
CopyMem (Queue->Buffer[Queue->Tail], Item, ItemSize);
//
// Adjust the tail pointer of the FIFO keyboard buffer.
//
Queue->Tail = (Queue->Tail + 1) % (MAX_KEY_ALLOWED + 1);
}
/**
Dequeue a item from the queue.
@param Queue Points to the queue.
@param Item Receives the item.
@param ItemSize Size of the item.
@retval EFI_SUCCESS Item was successfully dequeued.
@retval EFI_DEVICE_ERROR The queue is empty.
**/
EFI_STATUS
Dequeue (
IN OUT I2C_SIMPLE_QUEUE *Queue,
OUT VOID *Item,
IN UINTN ItemSize
)
{
ASSERT (Queue->ItemSize == ItemSize);
if (IsQueueEmpty (Queue)) {
return EFI_DEVICE_ERROR;
}
CopyMem (Item, Queue->Buffer[Queue->Head], ItemSize);
//
// Adjust the head pointer of the FIFO keyboard buffer.
//
Queue->Head = (Queue->Head + 1) % (MAX_KEY_ALLOWED + 1);
return EFI_SUCCESS;
}
/**
Sets monitor keys for SCU (SETUP_HOT_KEY / BOOT_MANAGER_HOT_KEY...)
@param KeyboardBuffer Points to the Keyboard Buffer.
@param Key Key code
**/
VOID
SetMonitorKey (
IN I2C_KB_DEV *KbDev,
IN UINT8 Key
)
{
UINT8 *FilterKeyList;
UINT8 ModKey;
UINTN Index;
UINTN BitIndex;
UINT16 KbcScanCode;
//
// Skip if no MonitorKey installed
//
if (EBDA(EBDA_MONITOR_KEY_TABLE) != 0x55 || Key >= 0xe0) return;
//
// Convert key code to KBC key code
//
KbcScanCode = ConvertKbcScanCode (KbDev, Key);
if ((KbcScanCode & 0xff) == 0xe0) {
//
// Convert double code back to single code
//
KbcScanCode >>= 8;
KbcScanCode |= 0x80;
}
//
// Get FilterKeyList pointer
//
FilterKeyList = (UINT8*)(UINTN)((*(UINT16*)&(EBDA(EBDA_MONITOR_KEY_TABLE + 3)) << 4) + *(UINT16*)&(EBDA(EBDA_MONITOR_KEY_TABLE + 1)));
for (Index = 0, BitIndex = 0; FilterKeyList[Index] != 0; Index += 3, BitIndex ++) {
if (FilterKeyList[Index] == (UINT8)KbcScanCode) {
//
// Also checks Shift/Alt/Ctrl keys
//
if ((ModKey = FilterKeyList[Index + 1]) != 0) {
if (((ModKey & 0x01) && !KbDev->ShiftOn) ||
((ModKey & 0x02) && !KbDev->AltOn) ||
((ModKey & 0x04) && !KbDev->CtrlOn)) {
continue;
}
}
*(UINT32*)&(EBDA(EBDA_MONITOR_KEY_TABLE + 5)) |= (1 << BitIndex);
break;
}
}
}
/**
Sync the control key status to CSM environment
@param KeyboardBuffer Points to the Keyboard Buffer.
@param Key Key code
@param Down Special key
@retval EFI_SUCCESS Success
**/
EFI_STATUS
SyncEfiKeyToCsmkey (
IN I2C_KB_DEV *KbDev,
IN UINT8 KeyCode,
IN UINT8 Down
)
{
QUALIFIER_STAT *QualifierStat;
QualifierStat = (QUALIFIER_STAT*)(UINTN)(BDA_QUALIFIER_STAT);
switch (KeyCode) {
case 0xe1:
if (Down) {
QualifierStat->LeftShift = 1;
} else {
QualifierStat->LeftShift = 0;
}
break;
case 0xe5:
if (Down) {
QualifierStat->RightShift = 1;
} else {
QualifierStat->RightShift = 0;
}
break;
case 0xe0:
case 0xe4:
if (Down) {
QualifierStat->Ctrl = 1;
} else {
QualifierStat->Ctrl = 0;
}
break;
case 0xe2:
case 0xe6:
if (Down) {
QualifierStat->Alt = 1;
} else {
QualifierStat->Alt = 0;
}
break;
default:
break;
}
return EFI_SUCCESS;
}
/**
Sync the control key status to EFI environment
@param KeyboardBuffer Points to the Keyboard Buffer.
@param Key Key code
@param Down Special key
@retval EFI_SUCCESS Success
**/
EFI_STATUS
SyncCsmKeyToEfikey (
IN I2C_KB_DEV *KbDev,
IN UINT8 KeyCode,
IN UINT8 Down
)
{
KEY Key;
Key.KeyCode = KeyCode;
Key.Down = Down;
Enqueue (&KbDev->I2cKeyQueue, &Key, sizeof (Key));
return EFI_SUCCESS;
}
/**
Inserts a key code into keyboard buffer.
@param KeyboardBuffer Points to the Keyboard Buffer.
@param Key Key code
@param Down Special key
@retval EFI_SUCCESS Success
**/
EFI_STATUS
InsertKeyCode (
IN I2C_KB_DEV *KbDev,
IN UINT8 KeyCode,
IN UINT8 Down
)
{
KEY Key;
//
// Ignore LED update if it is updating by Int9
//
if (KbDev->IsCsmEnabled && PS2_LED_UPDATING && (KeyCode == 0x53 || KeyCode == 0x39 || KeyCode == 0x47)) {
return EFI_SUCCESS;
}
Key.KeyCode = KeyCode;
Key.Down = Down;
Enqueue (&KbDev->I2cKeyQueue, &Key, sizeof (Key));
//
// Sets monitor keys for SCU (SETUP_HOT_KEY / BOOT_MANAGER_HOT_KEY...)
//
if (KbDev->IsCsmEnabled) {
SetMonitorKey(KbDev, KeyCode);
if (KeyCode >= 0xe0) {
SyncEfiKeyToCsmkey (KbDev, KeyCode, Down);
}
}
return EFI_SUCCESS;
}
/**
Timer handler for Repeat Key timer.
@param Context Points to the I2C_KB_DEV instance.
**/
VOID
KeyboardRepeatHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
I2C_KB_DEV *KbDev;
KbDev = (I2C_KB_DEV *) Context;
//
// Do nothing when there is no repeat key.
//
if (KbDev->RepeatKey != 0) {
//
// Inserts one Repeat key into keyboard buffer,
//
InsertKeyCode (
KbDev,
KbDev->RepeatKey,
1
);
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
KeyboardRepeatHandler,
KbDev,
&KbDev->RepeatPollingHandle
);
if (EFI_ERROR(Status)) {
return;
}
Status = gBS->SetTimer (
KbDev->RepeatPollingHandle,
TimerRelative,
KBD_REPEAT_RATE
);
if (EFI_ERROR(Status)) {
return;
}
}
}