alder_lake_bios/Insyde/InsydeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdCtrller.c

2320 lines
72 KiB
C

/** @file
;******************************************************************************
;* Copyright (c) 2012 - 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.
;*
;******************************************************************************
*/
/**
Routines that access 8042 keyboard controller
Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "Ps2Keyboard.h"
VOID
Ps2KeyboardCheckLEDHandler (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
);
#define IS_MAKE_CODE(ScanCode) ((BOOLEAN) (((ScanCode) & 0x80) == 0))
#define GET_MAKE_CODE(ScanCode) ((ScanCode) & 0x7F)
#define ATTR_EXTEND_0_TRUE BIT0
#define ATTR_EXTEND_0_FALSE BIT1
#define ATTR_EXTEND_1_TRUE BIT2
#define ATTR_EXTEND_1_FALSE BIT3
#define ATTR_EXTEND_0_DONT_CARE (ATTR_EXTEND_0_TRUE | ATTR_EXTEND_0_FALSE)
#define ATTR_EXTEND_1_DONT_CARE (ATTR_EXTEND_1_TRUE | ATTR_EXTEND_1_FALSE)
struct {
UINT8 ScanCode;
UINT8 PrefixScanCodeAttr;
}
mEfiKeyToPs2KeyCodeConvertTable[] = {
{SCANCODE_CTRL_MAKE , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_FALSE }, // EfiKeyLCtrl
{SCANCODE_LEFT_LOGO_MAKE , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_FALSE }, // EfiKeyA0
{SCANCODE_ALT_MAKE , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_FALSE }, // EfiKeyLAlt
{0x39 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeySpaceBar
{SCANCODE_ALT_MAKE , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_FALSE }, // EfiKeyA2
{SCANCODE_RIGHT_LOGO_MAKE , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_FALSE }, // EfiKeyA3
{SCANCODE_MENU_MAKE , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_FALSE }, // EfiKeyA4
{SCANCODE_CTRL_MAKE , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_FALSE }, // EfiKeyRCtrl
{0x4b , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyLeftArrow
{0x50 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyDownArrow
{0x4d , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyRightArrow
{0x52 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyZero
{0x53 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyPeriod
{0x1c , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyEnter
{SCANCODE_LEFT_SHIFT_MAKE , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_FALSE }, // EfiKeyLShift
{0x56 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB0
{0x2c , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB1
{0x2d , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB2
{0x2e , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB3
{0x2f , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB4
{0x30 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB5
{0x31 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB6
{0x32 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB7
{0x33 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB8
{0x34 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB9
{0x35 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyB10
{SCANCODE_RIGHT_SHIFT_MAKE, ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_FALSE }, // EfiKeyRShift
{0x48 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyUpArrow
{0x4f , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyOne
{0x50 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyTwo
{0x51 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyThree
{SCANCODE_CAPS_LOCK_MAKE , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_FALSE }, // EfiKeyCapsLock
{0x1e , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC1
{0x1f , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC2
{0x20 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC3
{0x21 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC4
{0x22 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC5
{0x23 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC6
{0x24 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC7
{0x25 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC8
{0x26 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC9
{0x27 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC10
{0x28 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC11
{0x2b , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyC12 (ScanCode is the same as EfiKeyD13)
{0x4b , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyFour
{0x4c , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyFive
{0x4d , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeySix
{0x4e , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyPlus
{0x0F , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyTab
{0x10 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD1
{0x11 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD2
{0x12 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD3
{0x13 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD4
{0x14 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD5
{0x15 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD6
{0x16 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD7
{0x17 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD8
{0x18 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD9
{0x19 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD10
{0x1a , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD11
{0x1b , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD12
{0x2b , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyD13 (ScanCode is the same as EfiKeyC12)
{0x53 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyDel
{0x4f , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyEnd
{0x51 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyPgDn
{0x47 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeySeven
{0x48 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyEight
{0x49 , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyNine
{0x29 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE0
{0x02 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE1
{0x03 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE2
{0x04 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE3
{0x05 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE4
{0x06 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE5
{0x07 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE6
{0x08 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE7
{0x09 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE8
{0x0A , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE9
{0x0B , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE10
{0x0C , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE11
{0x0D , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyE12
{0x0E , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyBackSpace
{0x52 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyIns
{0x47 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyHome
{0x49 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyPgUp
{SCANCODE_NUM_LOCK_MAKE , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_FALSE }, // EfiKeyNLck
{0x35 , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeySlash
{SCANCODE_SYS_REQ_MAKE , ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyAsterisk
{0x4a , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyMinus
{0x01 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyEsc
{0x3B , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF1
{0x3C , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF2
{0x3D , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF3
{0x3E , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF4
{0x3F , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF5
{0x40 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF6
{0x41 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF7
{0x42 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF8
{0x43 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF9
{0x44 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF10
{0x57 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF11
{0x58 , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyF12
{SCANCODE_LEFT_SHIFT_MAKE , ATTR_EXTEND_0_TRUE | ATTR_EXTEND_1_DONT_CARE}, // EfiKeyPrint (multiple scan codes)
{SCANCODE_SCROLL_LOCK_MAKE, ATTR_EXTEND_0_FALSE | ATTR_EXTEND_1_FALSE }, // EfiKeySLck
{SCANCODE_NUM_LOCK_MAKE , ATTR_EXTEND_0_DONT_CARE | ATTR_EXTEND_1_TRUE }, // EfiKeyPause (multiple scan codes)
};
//
// Keyboard modifier value to EFI Scan Code convertion table
// EFI Scan Code and the modifier values are defined in UEFI spec.
//
UINT8 mModifierValueToEfiScanCodeConvertionTable[] = {
SCAN_NULL, // EFI_NULL_MODIFIER
SCAN_NULL, // EFI_LEFT_CONTROL_MODIFIER
SCAN_NULL, // EFI_RIGHT_CONTROL_MODIFIER
SCAN_NULL, // EFI_LEFT_ALT_MODIFIER
SCAN_NULL, // EFI_RIGHT_ALT_MODIFIER
SCAN_NULL, // EFI_ALT_GR_MODIFIER
SCAN_INSERT, // EFI_INSERT_MODIFIER
SCAN_DELETE, // EFI_DELETE_MODIFIER
SCAN_PAGE_DOWN, // EFI_PAGE_DOWN_MODIFIER
SCAN_PAGE_UP, // EFI_PAGE_UP_MODIFIER
SCAN_HOME, // EFI_HOME_MODIFIER
SCAN_END, // EFI_END_MODIFIER
SCAN_NULL, // EFI_LEFT_SHIFT_MODIFIER
SCAN_NULL, // EFI_RIGHT_SHIFT_MODIFIER
SCAN_NULL, // EFI_CAPS_LOCK_MODIFIER
SCAN_NULL, // EFI_NUM_LOCK_MODIFIER
SCAN_LEFT, // EFI_LEFT_ARROW_MODIFIER
SCAN_RIGHT, // EFI_RIGHT_ARROW_MODIFIER
SCAN_DOWN, // EFI_DOWN_ARROW_MODIFIER
SCAN_UP, // EFI_UP_ARROW_MODIFIER
SCAN_NULL, // EFI_NS_KEY_MODIFIER
SCAN_NULL, // EFI_NS_KEY_DEPENDENCY_MODIFIER
SCAN_F1, // EFI_FUNCTION_KEY_ONE_MODIFIER
SCAN_F2, // EFI_FUNCTION_KEY_TWO_MODIFIER
SCAN_F3, // EFI_FUNCTION_KEY_THREE_MODIFIER
SCAN_F4, // EFI_FUNCTION_KEY_FOUR_MODIFIER
SCAN_F5, // EFI_FUNCTION_KEY_FIVE_MODIFIER
SCAN_F6, // EFI_FUNCTION_KEY_SIX_MODIFIER
SCAN_F7, // EFI_FUNCTION_KEY_SEVEN_MODIFIER
SCAN_F8, // EFI_FUNCTION_KEY_EIGHT_MODIFIER
SCAN_F9, // EFI_FUNCTION_KEY_NINE_MODIFIER
SCAN_F10, // EFI_FUNCTION_KEY_TEN_MODIFIER
SCAN_F11, // EFI_FUNCTION_KEY_ELEVEN_MODIFIER
SCAN_F12, // EFI_FUNCTION_KEY_TWELVE_MODIFIER
//
// For Partial Keystroke support
//
SCAN_NULL, // EFI_PRINT_MODIFIER
SCAN_NULL, // EFI_SYS_REQUEST_MODIFIER
SCAN_NULL, // EFI_SCROLL_LOCK_MODIFIER
SCAN_PAUSE, // EFI_PAUSE_MODIFIER
SCAN_NULL, // EFI_BREAK_MODIFIER
SCAN_NULL, // EFI_LEFT_LOGO_MODIFIER
SCAN_NULL, // EFI_RIGHT_LOGO_MODIFER
SCAN_NULL, // EFI_MENU_MODIFER
};
STATIC
UINT8 CtrlIgnoreKey[] = {
0x02, // '1','!'
0x04, // '3','#'
0x05, // '4','$'
0x06, // '5','%'
0x08, // '7','&'
0x09, // '8','*'
0x0A, // '9','('
0x0B, // '0',')'
0x0D, // '=','+'
0x27, // ';',':'
0x2B, // '\'','"'
0x29, // '`','~'
0x33, // ',','<'
0x34, // '.','>'
0x35 // '/','?'
};
STATIC
UINT8 AltIgnoreKey[] = {
0xD3, // 'Delete Forward'
0xC8, // 'Up Arrow'
0xD0, // 'Down Arrow'
0xCB, // 'Left Arrow'
0xCD, // 'Right Arrow'
0x52 // 'keypad 0 Insert'
};
//
// The WaitForValue time out
//
UINTN mWaitForValueTimeOut = KEYBOARD_WAITFORVALUE_TIMEOUT;
BOOLEAN mEnableMouseInterface;
/**
Return the count of scancode in the queue.
@param Queue Pointer to instance of SCAN_CODE_QUEUE.
@return Count of the scancode.
**/
UINTN
GetScancodeBufCount (
IN SCAN_CODE_QUEUE *Queue
)
{
if (Queue->Head <= Queue->Tail) {
return Queue->Tail - Queue->Head;
} else {
return Queue->Tail + KEYBOARD_SCAN_CODE_MAX_COUNT - Queue->Head;
}
}
/**
Read several bytes from the scancode buffer without removing them.
This function is called to see if there are enough bytes of scancode
representing a single key.
@param Queue Pointer to instance of SCAN_CODE_QUEUE.
@param Count Number of bytes to be read
@param Buf Store the results
@retval EFI_SUCCESS success to scan the keyboard code
@retval EFI_NOT_READY invalid parameter
**/
EFI_STATUS
GetScancodeBufHead (
IN SCAN_CODE_QUEUE *Queue,
IN UINTN Count,
OUT UINT8 *Buf
)
{
UINTN Index;
UINTN Pos;
//
// check the valid range of parameter 'Count'
//
if (GetScancodeBufCount (Queue) < Count) {
return EFI_NOT_READY;
}
//
// retrieve the values
//
for (Index = 0, Pos = Queue->Head; Index < Count; Index++, Pos = (Pos + 1) % KEYBOARD_SCAN_CODE_MAX_COUNT) {
Buf[Index] = Queue->Buffer[Pos];
}
return EFI_SUCCESS;
}
/**
Read & remove several bytes from the scancode buffer.
This function is usually called after GetScancodeBufHead()
@param Queue Pointer to instance of SCAN_CODE_QUEUE.
@param Count Number of bytes to be read
@param Buf Store the results
@retval EFI_SUCCESS success to scan the keyboard code
@retval EFI_NOT_READY invalid parameter
**/
EFI_STATUS
PopScancodeBufHead (
IN SCAN_CODE_QUEUE *Queue,
IN UINTN Count,
OUT UINT8 *Buf OPTIONAL
)
{
UINTN Index;
//
// Check the valid range of parameter 'Count'
//
if (GetScancodeBufCount (Queue) < Count) {
return EFI_NOT_READY;
}
//
// Retrieve and remove the values
//
for (Index = 0; Index < Count; Index++, Queue->Head = (Queue->Head + 1) % KEYBOARD_SCAN_CODE_MAX_COUNT) {
if (Buf != NULL) {
Buf[Index] = Queue->Buffer[Queue->Head];
}
}
return EFI_SUCCESS;
}
/**
Push one byte to the scancode buffer.
@param Queue Pointer to instance of SCAN_CODE_QUEUE.
@param Scancode The byte to push.
**/
VOID
PushScancodeBufTail (
IN SCAN_CODE_QUEUE *Queue,
IN UINT8 Scancode
)
{
if (GetScancodeBufCount (Queue) == KEYBOARD_SCAN_CODE_MAX_COUNT - 1) {
PopScancodeBufHead (Queue, 1, NULL);
}
Queue->Buffer[Queue->Tail] = Scancode;
Queue->Tail = (Queue->Tail + 1) % KEYBOARD_SCAN_CODE_MAX_COUNT;
}
/**
Read data register .
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@return return the value
**/
UINT8
KeyReadDataRegister (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
EFI_ISA_IO_PROTOCOL *IsaIo;
UINT8 Data;
//
// Use IsaIo protocol to perform IO operations
//
IsaIo = ConsoleIn->IsaIo;
IsaIo->Io.Read (
IsaIo,
EfiIsaIoWidthUint8,
ConsoleIn->DataRegisterAddress,
1,
&Data
);
return Data;
}
/**
Write data register.
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@param Data value wanted to be written
**/
VOID
KeyWriteDataRegister (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN UINT8 Data
)
{
ConsoleIn->IsaIo->Io.Write (
ConsoleIn->IsaIo,
EfiIsaIoWidthUint8,
ConsoleIn->DataRegisterAddress,
1,
&Data
);
}
/**
Read status register.
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@return value in status register
**/
UINT8
KeyReadStatusRegister (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
UINT8 Data;
ConsoleIn->IsaIo->Io.Read (
ConsoleIn->IsaIo,
EfiIsaIoWidthUint8,
ConsoleIn->StatusRegisterAddress,
1,
&Data
);
return Data;
}
/**
Write command register .
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@param Data The value wanted to be written
**/
VOID
KeyWriteCommandRegister (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN UINT8 Data
)
{
ConsoleIn->IsaIo->Io.Write (
ConsoleIn->IsaIo,
EfiIsaIoWidthUint8,
ConsoleIn->CommandRegisterAddress,
1,
&Data
);
}
/**
Display error message.
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@param ErrMsg Unicode string of error message
**/
VOID
KeyboardError (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN CHAR16 *ErrMsg
)
{
ConsoleIn->KeyboardErr = TRUE;
}
/**
Timer event handler: read a series of scancodes from 8042
and put them into memory scancode buffer.
it read as much scancodes to either fill
the memory buffer or empty the keyboard buffer.
It is registered as running under TPL_NOTIFY
@param Event The timer event
@param Context A KEYBOARD_CONSOLE_IN_DEV pointer
**/
VOID
EFIAPI
KeyboardTimerHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINT8 Data;
EFI_TPL OldTpl;
KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
ConsoleIn = (KEYBOARD_CONSOLE_IN_DEV *) Context;
//
// Enter critical section
//
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
if (((KEYBOARD_CONSOLE_IN_DEV *) Context)->KeyboardErr) {
//
// Leave critical section and return
//
gBS->RestoreTPL (OldTpl);
return ;
}
//
// To let KB driver support Hot plug, here should skip the 'resend' command for the case that
// KB is not connected to system. If KB is not connected to system, driver will find there's something
// error in the following code and wait for the input buffer empty, this waiting time shoulb be short enough since
// this is a NOTIFY TPL period function, or the system performance will degrade hardly when KB is not connected.
// Just skip the 'resend' process simply.
//
while ((KeyReadStatusRegister (ConsoleIn) & (KEYBOARD_STATUS_REGISTER_TRANSMIT_TIMEOUT|KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA)) ==
KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA
) {
//
// Stall 10 microsecond between the reading of KBC status and data port to make KBC has
// enough time to prepare correct scan code in the data port
//
gBS->Stall (10);
//
// Read one byte of the scan code and store it into the memory buffer
//
Data = KeyReadDataRegister (ConsoleIn);
PushScancodeBufTail (&ConsoleIn->ScancodeQueue, Data);
}
KeyGetchar (ConsoleIn);
//
// Leave critical section and return
//
gBS->RestoreTPL (OldTpl);
}
/**
Read key value .
@param ConsoleIn - Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@param Data - Pointer to outof buffer for keeping key value
@retval EFI_TIMEOUT Status resigter time out
@retval EFI_SUCCESS Success to read keyboard
**/
EFI_STATUS
KeyboardRead (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
OUT UINT8 *Data
)
{
UINT32 TimeOut;
UINT32 RegFilled;
TimeOut = 0;
RegFilled = 0;
//
// wait till output buffer full then perform the read
//
for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) {
if (KeyReadStatusRegister (ConsoleIn) & KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA) {
RegFilled = 1;
*Data = KeyReadDataRegister (ConsoleIn);
break;
}
MicroSecondDelay (30);
}
if (RegFilled == 0) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
/**
write key to keyboard
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@param Data value wanted to be written
@retval EFI_TIMEOUT The input buffer register is full for putting new value util timeout
@retval EFI_SUCCESS The new value is sucess put into input buffer register.
**/
EFI_STATUS
KeyboardWrite (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN UINT8 Data
)
{
UINT32 TimeOut;
UINT32 RegEmptied;
TimeOut = 0;
RegEmptied = 0;
//
// wait for input buffer empty
//
for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) {
if ((KeyReadStatusRegister (ConsoleIn) & 0x02) == 0) {
RegEmptied = 1;
break;
}
MicroSecondDelay (30);
}
if (RegEmptied == 0) {
return EFI_TIMEOUT;
}
//
// Write it
//
KeyWriteDataRegister (ConsoleIn, Data);
return EFI_SUCCESS;
}
/**
Issue keyboard command.
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@param Data The buff holding the command
@retval EFI_TIMEOUT Keyboard is not ready to issuing
@retval EFI_SUCCESS Success to issue keyboard command
**/
EFI_STATUS
KeyboardCommand (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN UINT8 Data
)
{
UINT32 TimeOut;
UINT32 RegEmptied;
TimeOut = 0;
RegEmptied = 0;
//
// Wait For Input Buffer Empty
//
for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) {
if ((KeyReadStatusRegister (ConsoleIn) & 0x02) == 0) {
RegEmptied = 1;
break;
}
MicroSecondDelay (30);
}
if (RegEmptied == 0) {
return EFI_TIMEOUT;
}
//
// issue the command
//
KeyWriteCommandRegister (ConsoleIn, Data);
//
// Wait For Input Buffer Empty again
//
RegEmptied = 0;
for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) {
if ((KeyReadStatusRegister (ConsoleIn) & 0x02) == 0) {
RegEmptied = 1;
break;
}
MicroSecondDelay (30);
}
if (RegEmptied == 0) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
/**
wait for a specific value to be presented on
8042 Data register by keyboard and then read it,
used in keyboard commands ack
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@param Value the value wanted to be waited.
@retval EFI_TIMEOUT Fail to get specific value in given time
@retval EFI_SUCCESS Success to get specific value in given time.
**/
EFI_STATUS
KeyboardWaitForValue (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN UINT8 Value
)
{
UINT8 Data;
UINT32 TimeOut;
UINT32 SumTimeOut;
UINT32 GotIt;
GotIt = 0;
TimeOut = 0;
SumTimeOut = 0;
//
// Make sure the initial value of 'Data' is different from 'Value'
//
Data = 0;
if (Data == Value) {
Data = 1;
}
//
// Read from 8042 (multiple times if needed)
// until the expected value appears
// use SumTimeOut to control the iteration
//
while (1) {
//
// Perform a read
//
for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) {
if (KeyReadStatusRegister (ConsoleIn) & 0x01) {
Data = KeyReadDataRegister (ConsoleIn);
break;
}
MicroSecondDelay (30);
}
SumTimeOut += TimeOut;
if (Data == Value) {
GotIt = 1;
break;
}
if (SumTimeOut >= mWaitForValueTimeOut) {
break;
}
}
//
// Check results
//
if (GotIt == 1) {
return EFI_SUCCESS;
} else {
return EFI_TIMEOUT;
}
}
/**
wait for a specific value to be presented on
8042 Data register by keyboard and then read it,
used in keyboard commands ack or failed ack
@param[in] ConsoleIn The KEYBOARD_CONSOLE_IN_DEV instance pointer
@param[in] Values The any of the values to be waited for.
@param[in] NumOfValues Number of values to be waited for.
@param[out] Value Output buffer to save the value which receive from data port and the value
is one of the "Values".
@retval EFI_SUCCESS Success to get any of specific values in given time.
@retval EFI_TIMEOUT Fail to get any of specific values in given time.
**/
STATIC
EFI_STATUS
KeyboardWaitForValues (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN UINT8 *Values,
IN UINTN NumOfValues,
OUT UINT8 *Value
)
{
UINT8 Data;
UINT32 TimeOut;
UINT32 SumTimeOut;
BOOLEAN ReceiveData;
UINTN Index;
SumTimeOut = 0;
Data = 0;
ReceiveData = FALSE;
//
// Read from 8042 (multiple times if needed)
// until the expected value appears
// use SumTimeOut to control the iteration
//
while (TRUE) {
//
// Perform a read
//
for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) {
if (KeyReadStatusRegister (ConsoleIn) & 0x01) {
Data = KeyReadDataRegister (ConsoleIn);
ReceiveData = TRUE;
break;
}
gBS->Stall (30);
}
SumTimeOut += TimeOut;
if (ReceiveData) {
for (Index = 0; Index < NumOfValues; Index++) {
if(Values[Index] == Data) {
*Value = Data;
return EFI_SUCCESS;
}
}
ReceiveData = FALSE;
}
if (SumTimeOut >= mWaitForValueTimeOut) {
break;
}
}
return EFI_TIMEOUT;
}
/**
Show keyboard status lights according to
indicators in ConsoleIn.
@param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV
@return status of updating keyboard register
**/
EFI_STATUS
UpdateStatusLights (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
EFI_STATUS Status;
UINT8 Command;
UINT8 Values[2];
UINT8 Value;
//
// Send keyboard command
//
Status = KeyboardWrite (ConsoleIn, 0xed);
if (EFI_ERROR (Status)) {
return Status;
}
//
// If system doesn't plug-in keyboard to PS/2 connector, some KBC will return KBC_CMDECHO_FAILED_ACK
//
Value = 0;
Values[0] = KEYBOARD_8048_RETURN_8042_ACK;
Values[1] = KEYBOARD_8048_RETURN_8042_FAILED_ACK;
Status = KeyboardWaitForValues (ConsoleIn, Values, sizeof (Values), &Value);
if (EFI_ERROR (Status) || Value == KEYBOARD_8048_RETURN_8042_FAILED_ACK) {
return EFI_SUCCESS;
}
//
// Light configuration
//
Command = 0;
if (ConsoleIn->CapsLock) {
Command |= 4;
}
if (ConsoleIn->NumLock) {
Command |= 2;
}
if (ConsoleIn->ScrollLock) {
Command |= 1;
}
Status = KeyboardWrite (ConsoleIn, Command);
if (EFI_ERROR (Status)) {
return Status;
}
Status = KeyboardWaitForValue (ConsoleIn, 0xfa);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "KeyboardWaitForValue() failed - %r\n", Status));
}
return EFI_SUCCESS;
}
/**
Check if the scan code is the break code of EFI_KEY_STATE.KeyShiftState.
@param[in] Extend1 Indicate if there is a prefix scan code E1
@param[in] ScanCode PS2 scan code
@retval TRUE The scan code is the break code of EFI_KEY_STATE.KeyShiftState.
@retval FALSE The scan code is not the break code of EFI_KEY_STATE.KeyShiftState.
**/
STATIC
BOOLEAN
IsBreakCodeOfEfiKeyShiftState (
IN BOOLEAN Extend1,
IN UINT16 ScanCode
)
{
if (!Extend1 &&
(ScanCode == SCANCODE_CTRL_BREAK ||
ScanCode == SCANCODE_ALT_BREAK ||
ScanCode == SCANCODE_LEFT_SHIFT_BREAK ||
ScanCode == SCANCODE_RIGHT_SHIFT_BREAK ||
ScanCode == SCANCODE_LEFT_LOGO_BREAK ||
ScanCode == SCANCODE_RIGHT_LOGO_BREAK ||
ScanCode == SCANCODE_MENU_BREAK ||
ScanCode == SCANCODE_SYS_REQ_BREAK_WITH_ALT ||
ScanCode == SCANCODE_SYS_REQ_BREAK)) {
return TRUE;
}
return FALSE;
}
/**
Convert PS2 scan code to EFI_KEY value.
@param[in] Extend0 Indicate if there is a prefix scan code E0
@param[in] Extend1 Indicate if there is a prefix scan code E1
@param[in] ScanCode PS2 scan code
@return The EFI_KEY value or oxFF if not found.
**/
STATIC
EFI_KEY
ConvertPs2ScanCodeToEfiKey (
IN BOOLEAN Extend0,
IN BOOLEAN Extend1,
IN UINT16 ScanCode
)
{
UINTN Index;
if (Extend0 && ScanCode == SCANCODE_SCROLL_LOCK_MAKE) {
//
// PAUSE shares the same scancode as that of SCROLL except PAUSE (CTRL pressed) has E0 prefix
//
return EfiKeyPause;
} else if ((!Extend1 && Extend0 && ScanCode == SCANCODE_SYS_REQ_MAKE) ||
(!Extend1 && ScanCode == SCANCODE_SYS_REQ_MAKE_WITH_ALT)) {
//
// PRNT_SCRN shares the same scancode as that of Key Pad "*" except PRNT_SCRN has E0 prefix
//
return EfiKeyPrint;
} else {
//
// Except the above special case, all others can be handled by convert table
//
for (Index = 0; Index < ARRAY_SIZE(mEfiKeyToPs2KeyCodeConvertTable); Index++) {
if ((ScanCode == mEfiKeyToPs2KeyCodeConvertTable[Index].ScanCode) &&
((Extend0 && (mEfiKeyToPs2KeyCodeConvertTable[Index].PrefixScanCodeAttr & ATTR_EXTEND_0_TRUE ) != 0) ||
(!Extend0 && (mEfiKeyToPs2KeyCodeConvertTable[Index].PrefixScanCodeAttr & ATTR_EXTEND_0_FALSE) != 0)) &&
((Extend1 && (mEfiKeyToPs2KeyCodeConvertTable[Index].PrefixScanCodeAttr & ATTR_EXTEND_1_TRUE ) != 0) ||
(!Extend1 && (mEfiKeyToPs2KeyCodeConvertTable[Index].PrefixScanCodeAttr & ATTR_EXTEND_1_FALSE) != 0))) {
return (EFI_KEY) Index;
}
}
}
return 0xFF;
}
/**
Convert EFI_KEY value to corresponding key descriptor from input keyboard layout.
@param[in] KbLayout Pointer to keyboard layout
@param[in] EfiKey The EFI_KEY value.
@return The pointer of key descriptor or NULL if not found.
**/
EFI_KEY_DESCRIPTOR *
ConvertEfiKeyToKeyDescriptor (
IN EFI_HII_KEYBOARD_LAYOUT *KbLayout,
IN EFI_KEY EfiKey
)
{
EFI_KEY_DESCRIPTOR *KeyDescriptor;
UINTN Index;
KeyDescriptor = (EFI_KEY_DESCRIPTOR *) (((UINT8 *) KbLayout) + sizeof (EFI_HII_KEYBOARD_LAYOUT));
for (Index = 0; Index < KbLayout->DescriptorCount; Index++) {
if (KeyDescriptor[Index].Key == EfiKey) {
return &KeyDescriptor[Index];
}
//
// For non-spacing key, skip following physical keys.
//
if (KeyDescriptor[Index].Modifier == EFI_NS_KEY_MODIFIER) {
while (((Index + 1) < KbLayout->DescriptorCount) &&
(KeyDescriptor[Index + 1].Modifier == EFI_NS_KEY_DEPENDENCY_MODIFIER)) {
Index++;
}
}
}
return NULL;
}
/**
Get corresponding physical key descriptor by non-spacing and current key descriptors.
@param[in] KbLayout Pointer to keyboard layout
@param[in] CurrentNsKey Pointer to non-spacing key descriptor
@param[in] CurrentKeyDescriptor Pointer to current key descriptor
@return The pointer of corresponding physical key descriptor or original key descriptor (i.e. CurrentKeyDescriptor) if not found.
**/
EFI_KEY_DESCRIPTOR *
Ps2GetNsPhysicalKey (
IN EFI_HII_KEYBOARD_LAYOUT *KbLayout,
IN EFI_KEY_DESCRIPTOR *CurrentNsKey,
IN EFI_KEY_DESCRIPTOR *CurrentKeyDescriptor
)
{
EFI_KEY_DESCRIPTOR *KeyDescriptor;
UINTN Index;
KeyDescriptor = (EFI_KEY_DESCRIPTOR *) (((UINT8 *) KbLayout) + sizeof (EFI_HII_KEYBOARD_LAYOUT));
for (Index = 0; Index < KbLayout->DescriptorCount; Index++, KeyDescriptor++) {
if (KeyDescriptor != CurrentNsKey) {
continue;
}
//
// Find non-spacing key and then find the corresponding physical keys.
//
ASSERT (KeyDescriptor->Modifier == EFI_NS_KEY_MODIFIER);
for (KeyDescriptor++, Index++; Index < KbLayout->DescriptorCount; KeyDescriptor++, Index++) {
if (KeyDescriptor->Modifier != EFI_NS_KEY_DEPENDENCY_MODIFIER) {
break;
}
if (KeyDescriptor->Key == CurrentKeyDescriptor->Key) {
return KeyDescriptor;
}
}
break;
}
//
// No children definition matched, return original key
//
return CurrentKeyDescriptor;
}
/**
Get key descriptor by PS2 scan code.
@param[in] KbLayout Pointer to keyboard layout
@param[in] Extend0 Indicate if there is a prefix scan code E0
@param[in] Extend1 Indicate if there is a prefix scan code E1
@param[in] ScanCode PS2 scan code
@return The pointer of key descriptor or NULL if not found.
**/
EFI_KEY_DESCRIPTOR *
Ps2GetKeyDescriptor (
IN EFI_HII_KEYBOARD_LAYOUT *KbLayout,
IN BOOLEAN Extend0,
IN BOOLEAN Extend1,
IN UINT16 ScanCode
)
{
EFI_KEY EfiKey;
EFI_KEY_DESCRIPTOR *KeyDescriptor;
if (KbLayout == NULL) {
return NULL;
}
EfiKey = ConvertPs2ScanCodeToEfiKey (Extend0, Extend1, ScanCode);
if (EfiKey == 0xFF) {
return NULL;
}
KeyDescriptor = ConvertEfiKeyToKeyDescriptor (KbLayout, EfiKey);
if (KeyDescriptor == NULL && EfiKey == EfiKeyC12) {
//
// Because it can not distinguish EfiKeyC12 and EfiKeyD13 (which PS2 scan codes are both 0x2b),
// try to get the key descriptior of EfiKeyD13 if key descriptior of EfiKeyC12 is not found.
//
KeyDescriptor = ConvertEfiKeyToKeyDescriptor (KbLayout, EfiKeyD13);
}
return KeyDescriptor;
}
/**
The notification function for EFI_HII_SET_KEYBOARD_LAYOUT_EVENT_GUID.
This function is registered to event of EFI_HII_SET_KEYBOARD_LAYOUT_EVENT_GUID
group type, which will be triggered by EFI_HII_DATABASE_PROTOCOL.SetKeyboardLayout().
It tries to get curent keyboard layout from HII database.
@param[in] Event Event being signaled.
@param[in] Context Points to KEYBOARD_CONSOLE_IN_DEV instance.
**/
VOID
EFIAPI
Ps2SetKeyboardLayoutEvent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
EFI_HII_KEYBOARD_LAYOUT *KeyboardLayout;
ConsoleIn = (KEYBOARD_CONSOLE_IN_DEV *) Context;
if (ConsoleIn->Signature != KEYBOARD_CONSOLE_IN_DEV_SIGNATURE) {
return;
}
KeyboardLayout = HiiGetCurrentKeyboardLayout ();
if (KeyboardLayout == NULL) {
return;
}
if (ConsoleIn->CurrentKbLayout != NULL) {
FreePool (ConsoleIn->CurrentKbLayout);
}
ConsoleIn->CurrentKbLayout = KeyboardLayout;
ConsoleIn->CurrentNsKey = NULL;
}
/**
Initialize the key state.
@param ConsoleIn The KEYBOARD_CONSOLE_IN_DEV instance.
@param KeyState A pointer to receive the key state information.
**/
VOID
InitializeKeyState (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
OUT EFI_KEY_STATE *KeyState
)
{
KeyState->KeyShiftState = EFI_SHIFT_STATE_VALID
| (ConsoleIn->LeftCtrl ? EFI_LEFT_CONTROL_PRESSED : 0)
| (ConsoleIn->RightCtrl ? EFI_RIGHT_CONTROL_PRESSED : 0)
| (ConsoleIn->LeftAlt ? EFI_LEFT_ALT_PRESSED : 0)
| (ConsoleIn->RightAlt ? EFI_RIGHT_ALT_PRESSED : 0)
| (ConsoleIn->LeftShift ? EFI_LEFT_SHIFT_PRESSED : 0)
| (ConsoleIn->RightShift ? EFI_RIGHT_SHIFT_PRESSED : 0)
| (ConsoleIn->LeftLogo ? EFI_LEFT_LOGO_PRESSED : 0)
| (ConsoleIn->RightLogo ? EFI_RIGHT_LOGO_PRESSED : 0)
| (ConsoleIn->Menu ? EFI_MENU_KEY_PRESSED : 0)
| (ConsoleIn->SysReq ? EFI_SYS_REQ_PRESSED : 0)
;
KeyState->KeyToggleState = EFI_TOGGLE_STATE_VALID
| (ConsoleIn->CapsLock ? EFI_CAPS_LOCK_ACTIVE : 0)
| (ConsoleIn->NumLock ? EFI_NUM_LOCK_ACTIVE : 0)
| (ConsoleIn->ScrollLock ? EFI_SCROLL_LOCK_ACTIVE : 0)
| (ConsoleIn->IsSupportPartialKey ? EFI_KEY_STATE_EXPOSED : 0)
;
}
/**
Get scancode from scancode buffer and translate into EFI-scancode and unicode defined by EFI spec.
The function is always called in TPL_NOTIFY.
@param ConsoleIn KEYBOARD_CONSOLE_IN_DEV instance pointer
**/
VOID
KeyGetchar (
IN OUT KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
EFI_STATUS Status;
UINT16 ScanCode;
BOOLEAN Extend0;
BOOLEAN Extend1;
UINTN Index;
EFI_KEY_DATA KeyData;
LIST_ENTRY *Link;
KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
//
// 3 bytes most
//
UINT8 ScancodeArr[3];
UINT32 ScancodeArrPos;
STATIC BOOLEAN ClearUnexpectedLShift = FALSE;
EFI_KEY_DESCRIPTOR *KeyDescriptor;
//
// Check if there are enough bytes of scancode representing a single key
// available in the buffer
//
while (TRUE) {
Extend0 = FALSE;
Extend1 = FALSE;
ScancodeArrPos = 0;
KeyDescriptor = NULL;
Status = GetScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr);
if (EFI_ERROR (Status)) {
return ;
}
if (ScancodeArr[ScancodeArrPos] == SCANCODE_EXTENDED0) {
//
// E0 to look ahead 2 bytes
//
Extend0 = TRUE;
ScancodeArrPos = 1;
Status = GetScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr);
if (EFI_ERROR (Status)) {
return ;
}
} else if (ScancodeArr[ScancodeArrPos] == SCANCODE_EXTENDED1) {
//
// E1 to look ahead 3 bytes
//
Extend1 = TRUE;
ScancodeArrPos = 2;
Status = GetScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr);
if (EFI_ERROR (Status)) {
return ;
}
}
//
// if we reach this position, scancodes for a key is in buffer now,pop them
//
Status = PopScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr);
ASSERT_EFI_ERROR (Status);
//
// store the last available byte, this byte of scancode will be checked
//
ScanCode = ScancodeArr[ScancodeArrPos];
if (!Extend1) {
switch (ScanCode) {
case SCANCODE_LEFT_SHIFT_MAKE:
if (Extend0) {
//
// To avoid recognize PRNT_SCRN key as a L_SHIFT key
// because PRNT_SCRN key generates E0 followed by L_SHIFT scan code.
// If it the second byte of the PRNT_ScRN skip it.
//
continue;
}
case SCANCODE_LEFT_SHIFT_BREAK:
if (Extend0) {
if (ClearUnexpectedLShift) {
//
// If receive scan codes 2A (LShift) E0 4B (left arrow) E0 4B (left arrow break) E0 AA, clear unexpected LShift bit because KBC lost to send E0 key.
//
ConsoleIn->LeftShift = FALSE;
}
}
ClearUnexpectedLShift = FALSE;
break;
case SCANCODE_INSERT_MAKE:
case SCANCODE_DELETE_MAKE:
case SCANCODE_LEFT_ARROW_MAKE:
case SCANCODE_HOME_MAKE:
case SCANCODE_END_MAKE:
case SCANCODE_UP_ARROW_MAKE:
case SCANCODE_DOWN_ARROW_MAKE:
case SCANCODE_PAGE_UP_MAKE:
case SCANCODE_PAGE_DOWN_MAKE:
case SCANCODE_RIGHT_ARROW_MAKE:
if (Extend0) {
if (ConsoleIn->LeftShift) {
ClearUnexpectedLShift = TRUE;
}
}
break;
}
}
if ((ScanCode >= SCANCODE_MAX_MAKE) && !IsBreakCodeOfEfiKeyShiftState (Extend1, ScanCode)) {
continue;
}
KeyDescriptor = Ps2GetKeyDescriptor (ConsoleIn->CurrentKbLayout, Extend0, Extend1, GET_MAKE_CODE(ScanCode));
if (KeyDescriptor == NULL) {
return;
}
switch (KeyDescriptor->Modifier) {
case EFI_LEFT_CONTROL_MODIFIER:
ConsoleIn->LeftCtrl = IS_MAKE_CODE(ScanCode);
break;
case EFI_RIGHT_CONTROL_MODIFIER:
ConsoleIn->RightCtrl = IS_MAKE_CODE(ScanCode);
break;
case EFI_LEFT_SHIFT_MODIFIER:
ConsoleIn->LeftShift = IS_MAKE_CODE(ScanCode);
break;
case EFI_RIGHT_SHIFT_MODIFIER:
ConsoleIn->RightShift = IS_MAKE_CODE(ScanCode);
break;
case EFI_LEFT_ALT_MODIFIER:
ConsoleIn->LeftAlt = IS_MAKE_CODE(ScanCode);
break;
case EFI_RIGHT_ALT_MODIFIER:
ConsoleIn->RightAlt = IS_MAKE_CODE(ScanCode);
break;
case EFI_LEFT_LOGO_MODIFIER:
ConsoleIn->LeftLogo = IS_MAKE_CODE(ScanCode);
break;
case EFI_RIGHT_LOGO_MODIFIER:
ConsoleIn->RightLogo = IS_MAKE_CODE(ScanCode);
break;
case EFI_MENU_MODIFIER:
ConsoleIn->Menu = IS_MAKE_CODE(ScanCode);
break;
case EFI_PRINT_MODIFIER:
case EFI_SYS_REQUEST_MODIFIER:
ConsoleIn->SysReq = IS_MAKE_CODE(ScanCode);
break;
case EFI_ALT_GR_MODIFIER:
ConsoleIn->AltGrOn = IS_MAKE_CODE(ScanCode);
break;
case EFI_CAPS_LOCK_MODIFIER:
ConsoleIn->CapsLock = (BOOLEAN)!ConsoleIn->CapsLock;
Ps2KeyboardCheckLEDHandler (ConsoleIn);
break;
case EFI_NUM_LOCK_MODIFIER:
ConsoleIn->NumLock = (BOOLEAN)!ConsoleIn->NumLock;
Ps2KeyboardCheckLEDHandler (ConsoleIn);
break;
case EFI_SCROLL_LOCK_MODIFIER:
ConsoleIn->ScrollLock = (BOOLEAN)!ConsoleIn->ScrollLock;
Ps2KeyboardCheckLEDHandler (ConsoleIn);
break;
case EFI_NULL_MODIFIER:
//
// Special IME for French Layout keyboard
//
if (CompareGuid ((EFI_GUID *) (((UINT8 *) ConsoleIn->CurrentKbLayout) + sizeof (UINT16)), &gH2OUsbKeyboardFrenchLayoutKeyGuid)) {
if (KeyDescriptor->Key == EfiKeyD11) {
if (ConsoleIn->LeftShift || ConsoleIn->RightShift) {
if (ConsoleIn->CircumflexAccentShiftedOn == 0) {
ConsoleIn->CircumflexAccentShiftedOn = 1;
continue;
}
} else {
if (ConsoleIn->CircumflexAccentOn == 0) {
ConsoleIn->CircumflexAccentOn = 1;
continue;
}
}
}
if ((KeyDescriptor->Key == EfiKeyE2) && (ConsoleIn->AltGrOn)) {
if (ConsoleIn->TildeOn == 0) {
ConsoleIn->TildeOn = 1;
continue;
}
}
if ((KeyDescriptor->Key == EfiKeyE7) && (ConsoleIn->AltGrOn)) {
if (ConsoleIn->GraveAccentOn == 0) {
ConsoleIn->GraveAccentOn = 1;
continue;
}
}
}
break;
}
//
// If this is above the valid range, ignore it
//
if (ScanCode >= SCANCODE_MAX_MAKE) {
continue;
} else {
break;
}
}
//
// Handle Ctrl+Alt+Del hotkey
//
if ((ConsoleIn->LeftCtrl || ConsoleIn->RightCtrl) &&
(ConsoleIn->LeftAlt || ConsoleIn->RightAlt ) &&
KeyDescriptor->Modifier == EFI_DELETE_MODIFIER
) {
gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
}
if (KeyDescriptor->Modifier == EFI_NS_KEY_MODIFIER) {
//
// If this is a dead key with EFI_NS_KEY_MODIFIER, then record it and return.
//
ConsoleIn->CurrentNsKey = KeyDescriptor;
return;
}
if (ConsoleIn->CurrentNsKey != NULL) {
//
// If this keystroke follows a non-spacing key, then find the descriptor for corresponding
// physical key.
//
KeyDescriptor = Ps2GetNsPhysicalKey (ConsoleIn->CurrentKbLayout, ConsoleIn->CurrentNsKey, KeyDescriptor);
ConsoleIn->CurrentNsKey = NULL;
}
//
// Make sure modifier of Key Descriptor is in the valid range according to UEFI spec.
//
if (KeyDescriptor->Modifier >= (sizeof (mModifierValueToEfiScanCodeConvertionTable) / sizeof (UINT8))) {
return;
}
//
// Save the Shift/Toggle state
//
InitializeKeyState (ConsoleIn, &KeyData.KeyState);
KeyData.Key.ScanCode = mModifierValueToEfiScanCodeConvertionTable[KeyDescriptor->Modifier];
KeyData.Key.UnicodeChar = KeyDescriptor->Unicode;
if ((KeyDescriptor->AffectedAttribute & EFI_AFFECTED_BY_STANDARD_SHIFT)!= 0) {
if (ConsoleIn->LeftShift || ConsoleIn->RightShift) {
KeyData.Key.UnicodeChar = KeyDescriptor->ShiftedUnicode;
//
// 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 ((KeyDescriptor->Unicode != KeyDescriptor->ShiftedUnicode)) {
KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED);
}
if (ConsoleIn->AltGrOn) {
KeyData.Key.UnicodeChar = KeyDescriptor->ShiftedAltGrUnicode;
}
} else {
//
// Shift off
//
KeyData.Key.UnicodeChar = KeyDescriptor->Unicode;
if (ConsoleIn->AltGrOn) {
KeyData.Key.UnicodeChar = KeyDescriptor->AltGrUnicode;
}
}
}
//
// alphabetic key is affected by CapsLock State
//
if ((KeyDescriptor->AffectedAttribute & EFI_AFFECTED_BY_CAPS_LOCK) != 0) {
if (ConsoleIn->CapsLock) {
if (KeyData.Key.UnicodeChar == KeyDescriptor->Unicode) {
KeyData.Key.UnicodeChar = KeyDescriptor->ShiftedUnicode;
} else if (KeyData.Key.UnicodeChar == KeyDescriptor->ShiftedUnicode) {
KeyData.Key.UnicodeChar = KeyDescriptor->Unicode;
}
}
}
//
// Translate Unicode 0x1B (ESC) to EFI Scan Code
//
if (KeyData.Key.UnicodeChar == 0x1B && KeyData.Key.ScanCode == SCAN_NULL) {
KeyData.Key.ScanCode = SCAN_ESC;
KeyData.Key.UnicodeChar = CHAR_NULL;
}
if (ConsoleIn->LeftAlt || ConsoleIn->RightAlt) {
for (Index = 0; Index < sizeof (AltIgnoreKey); Index++) {
if (ScanCode == AltIgnoreKey[Index]) {
return;
}
}
//
// Alt + numpad 1~9
// numpad 1~9 scancode greater than 0x47
if ((ScanCode >= 0x47) &&
(KeyData.Key.UnicodeChar >= '1' && KeyData.Key.UnicodeChar <= '9')) {
KeyData.Key.UnicodeChar = KeyData.Key.UnicodeChar - '1' + 1;
}
//
// Alt + (Up, Down, Left, Right)
//
if (KeyData.Key.ScanCode >= SCAN_UP && KeyData.Key.ScanCode <= SCAN_PAGE_DOWN) {
KeyData.Key.ScanCode = SCAN_NULL;
}
}
if (ConsoleIn->CircumflexAccentShiftedOn) {
ConsoleIn->CircumflexAccentShiftedOn += 1;
if (KeyData.Key.UnicodeChar == 'a') {
KeyData.Key.UnicodeChar = 0xE4;
} else if (KeyData.Key.UnicodeChar == 'e') {
KeyData.Key.UnicodeChar = 0xEB;
} else if (KeyData.Key.UnicodeChar == 'i') {
KeyData.Key.UnicodeChar = 0xEF;
} else if (KeyData.Key.UnicodeChar == 'o') {
KeyData.Key.UnicodeChar = 0xF6;
} else if (KeyData.Key.UnicodeChar == 'u') {
KeyData.Key.UnicodeChar = 0xFC;
} else if (KeyData.Key.UnicodeChar == 'y') {
KeyData.Key.UnicodeChar = 0xFF;
} else if (KeyData.Key.UnicodeChar == 'A') {
KeyData.Key.UnicodeChar = 0xC4;
} else if (KeyData.Key.UnicodeChar == 'E') {
KeyData.Key.UnicodeChar = 0xCB;
} else if (KeyData.Key.UnicodeChar == 'I') {
KeyData.Key.UnicodeChar = 0xCF;
} else if (KeyData.Key.UnicodeChar == 'O') {
KeyData.Key.UnicodeChar = 0xD6;
} else if (KeyData.Key.UnicodeChar == 'U') {
KeyData.Key.UnicodeChar = 0xDC;
} else {
KeyData.Key.UnicodeChar = 0xA8;
}
if (ConsoleIn->CircumflexAccentShiftedOn > 3)
ConsoleIn->CircumflexAccentShiftedOn = 0;
}
if (ConsoleIn->CircumflexAccentOn) {
ConsoleIn->CircumflexAccentOn += 1;
if (KeyData.Key.UnicodeChar == 'a') {
KeyData.Key.UnicodeChar = 0xE2;
} else if (KeyData.Key.UnicodeChar == 'e') {
KeyData.Key.UnicodeChar = 0xEA;
} else if (KeyData.Key.UnicodeChar == 'i') {
KeyData.Key.UnicodeChar = 0xEE;
} else if (KeyData.Key.UnicodeChar == 'o') {
KeyData.Key.UnicodeChar = 0xF4;
} else if (KeyData.Key.UnicodeChar == 'u') {
KeyData.Key.UnicodeChar = 0xFB;
} else if (KeyData.Key.UnicodeChar == 'A') {
KeyData.Key.UnicodeChar = 0xC2;
} else if (KeyData.Key.UnicodeChar == 'E') {
KeyData.Key.UnicodeChar = 0xCA;
} else if (KeyData.Key.UnicodeChar == 'I') {
KeyData.Key.UnicodeChar = 0xCE;
} else if (KeyData.Key.UnicodeChar == 'O') {
KeyData.Key.UnicodeChar = 0xD4;
} else if (KeyData.Key.UnicodeChar == 'U') {
KeyData.Key.UnicodeChar = 0xDB;
} else {
KeyData.Key.UnicodeChar = '^';
}
if (ConsoleIn->CircumflexAccentOn > 3)
ConsoleIn->CircumflexAccentOn = 0;
}
if (ConsoleIn->TildeOn) {
ConsoleIn->TildeOn += 1;
if (KeyData.Key.UnicodeChar == 'a') {
KeyData.Key.UnicodeChar = 0xE3;
} else if (KeyData.Key.UnicodeChar == 'A') {
KeyData.Key.UnicodeChar = 0xC3;
} else if (KeyData.Key.UnicodeChar == 'n') {
KeyData.Key.UnicodeChar = 0xF1;
} else if (KeyData.Key.UnicodeChar == 'N') {
KeyData.Key.UnicodeChar = 0xD1;
} else if (KeyData.Key.UnicodeChar == 'o') {
KeyData.Key.UnicodeChar = 0xF5;
} else if (KeyData.Key.UnicodeChar == 'O') {
KeyData.Key.UnicodeChar = 0xD5;
} else {
KeyData.Key.UnicodeChar = '~';
}
if (ConsoleIn->TildeOn > 3)
ConsoleIn->TildeOn = 0;
}
if (ConsoleIn->GraveAccentOn) {
ConsoleIn->GraveAccentOn += 1;
if (KeyData.Key.UnicodeChar == 'a') {
KeyData.Key.UnicodeChar = 0xE0;
} else if (KeyData.Key.UnicodeChar == 'e') {
KeyData.Key.UnicodeChar = 0xE8;
} else if (KeyData.Key.UnicodeChar == 'i') {
KeyData.Key.UnicodeChar = 0xEC;
} else if (KeyData.Key.UnicodeChar == 'o') {
KeyData.Key.UnicodeChar = 0xF2;
} else if (KeyData.Key.UnicodeChar == 'u') {
KeyData.Key.UnicodeChar = 0xF9;
} else if (KeyData.Key.UnicodeChar == 'A') {
KeyData.Key.UnicodeChar = 0xC0;
} else if (KeyData.Key.UnicodeChar == 'E') {
KeyData.Key.UnicodeChar = 0xC8;
} else if (KeyData.Key.UnicodeChar == 'I') {
KeyData.Key.UnicodeChar = 0xCC;
} else if (KeyData.Key.UnicodeChar == 'O') {
KeyData.Key.UnicodeChar = 0xD2;
} else if (KeyData.Key.UnicodeChar == 'U') {
KeyData.Key.UnicodeChar = 0xD9;
} else {
KeyData.Key.UnicodeChar = 0x60;
}
if (ConsoleIn->GraveAccentOn> 3)
ConsoleIn->GraveAccentOn = 0;
}
if ((KeyDescriptor->AffectedAttribute & EFI_AFFECTED_BY_NUM_LOCK) != 0) {
//
// For key affected by NumLock, if NumLock is on and Shift is not pressed, then it means
// normal key, instead of original control key. So the ScanCode should be cleaned.
// Otherwise, it means control key, so preserve the EFI Scan Code and clear the unicode keycode.
//
if ((ConsoleIn->NumLock) && (!(ConsoleIn->LeftShift || ConsoleIn->RightShift))) {
KeyData.Key.ScanCode = SCAN_NULL;
} else {
KeyData.Key.UnicodeChar = CHAR_NULL;
}
}
if (ConsoleIn->LeftCtrl || ConsoleIn->RightCtrl) {
for (Index = 0; Index < sizeof (CtrlIgnoreKey); Index++) {
if (ScanCode == CtrlIgnoreKey[Index]) {
return;
}
}
}
if((Extend0) &&
(KeyData.Key.UnicodeChar != 0x00) &&
(KeyData.Key.UnicodeChar != 0x0d) &&
(KeyData.Key.UnicodeChar!= 0x2f)) {
KeyData.Key.UnicodeChar = 0x00;
}
//
// If the key can not be converted then just return.
//
if (KeyData.Key.ScanCode == SCAN_NULL && KeyData.Key.UnicodeChar == CHAR_NULL) {
if (!ConsoleIn->IsSupportPartialKey) {
return ;
}
}
//
// Invoke notification functions if exist
//
for (Link = GetFirstNode (&ConsoleIn->NotifyList); !IsNull (&ConsoleIn->NotifyList, Link); Link = GetNextNode (&ConsoleIn->NotifyList, Link)) {
CurrentNotify = CR (
Link,
KEYBOARD_CONSOLE_IN_EX_NOTIFY,
NotifyEntry,
KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
);
if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
CurrentNotify->KeyNotificationFn (&KeyData);
}
}
PushEfikeyBufTail (&ConsoleIn->EfiKeyQueue, &KeyData);
}
EFI_STATUS
CheckAuxStatus (
OUT BOOLEAN *AuxEnable
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN NumberOfHandles;
UINTN Index;
VOID *Protocol;
*AuxEnable = FALSE;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimplePointerProtocolGuid,
NULL,
&NumberOfHandles,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
for (Index = 0; Index < NumberOfHandles; Index++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiIsaIoProtocolGuid,
&Protocol
);
if (EFI_ERROR (Status)) {
continue;
}
*AuxEnable = TRUE;
break;
}
FreePool (HandleBuffer);
return EFI_SUCCESS;
}
/**
Perform 8042 controller and keyboard Initialization.
If ExtendedVerification is TRUE, do additional test for
the keyboard interface
@param ConsoleIn - KEYBOARD_CONSOLE_IN_DEV instance pointer
@param ExtendedVerification - indicates a thorough initialization
@retval EFI_DEVICE_ERROR Fail to init keyboard
@retval EFI_SUCCESS Success to init keyboard
**/
EFI_STATUS
InitKeyboard (
IN OUT KEYBOARD_CONSOLE_IN_DEV *ConsoleIn,
IN BOOLEAN ExtendedVerification
)
{
EFI_STATUS Status;
EFI_STATUS Status1;
UINT8 CommandByte;
EFI_PS2_POLICY_PROTOCOL *Ps2Policy;
UINT32 TryTime;
BOOLEAN MouseEnable;
Status = EFI_SUCCESS;
mEnableMouseInterface = TRUE;
TryTime = 0;
//
// Get Ps2 policy to set this
//
Ps2Policy = NULL;
Status = gBS->LocateProtocol (
&gEfiPs2PolicyProtocolGuid,
NULL,
(VOID **) &Ps2Policy
);
if (EFI_ERROR (Status)) {
Ps2Policy = NULL;
}
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_CLEAR_BUFFER,
ConsoleIn->DevicePath
);
//
// Perform a read to cleanup the Status Register's
// output buffer full bits within MAX TRY times
//
if ((KeyReadStatusRegister (ConsoleIn) & KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA)) {
while (!EFI_ERROR (Status) && TryTime < KEYBOARD_MAX_TRY) {
Status = KeyboardRead (ConsoleIn, &CommandByte);
TryTime ++;
}
}
//
// Exceed the max try times. The device may be error.
//
if (TryTime == KEYBOARD_MAX_TRY) {
Status = EFI_DEVICE_ERROR;
goto Done;
}
//
// We should disable mouse interface during the initialization process
// since mouse device output could block keyboard device output in the
// 60H port of 8042 controller.
//
// So if we are not initializing 8042 controller for the
// first time, we have to remember the previous mouse interface
// enabling state
//
// Test the system flag in to determine whether this is the first
// time initialization
//
if ((KeyReadStatusRegister (ConsoleIn) & KEYBOARD_STATUS_REGISTER_SYSTEM_FLAG) != 0) {
if (!PcdGetBool (PcdFastPS2Detection)) {
//
// 8042 controller is already setup (by myself or by mouse driver):
// See whether mouse interface is already enabled
// which determines whether we should enable it later
//
//
// Read the command byte of 8042 controller
//
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_READ);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"\n\r");
goto Done;
}
Status = KeyboardRead (ConsoleIn, &CommandByte);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"\n\r");
goto Done;
}
//
// Test the mouse enabling bit
//
if ((CommandByte & 0x20) != 0) {
mEnableMouseInterface = FALSE;
} else {
mEnableMouseInterface = TRUE;
}
} else {
mEnableMouseInterface = FALSE;
}
} else {
//
// 8042 controller is not setup yet:
// 8042 controller selftest;
// Don't enable mouse interface later.
//
//
// Disable keyboard and mouse interfaces
//
MouseEnable = FALSE;
CheckAuxStatus (&MouseEnable);
if (!PcdGetBool (PcdFastPS2Detection) && !MouseEnable) {
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_DISABLE_KEYBOARD_INTERFACE);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"\n\r");
goto Done;
}
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_DISABLE_MOUSE_INTERFACE);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"\n\r");
goto Done;
}
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_SELF_TEST,
ConsoleIn->DevicePath
);
//
// 8042 Controller Self Test
//
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_CONTROLLER_SELF_TEST);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r");
goto Done;
}
Status = KeyboardWaitForValue (ConsoleIn, 0x55);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller self test failed!\n\r");
goto Done;
}
}
//
// Don't enable mouse interface later
//
mEnableMouseInterface = FALSE;
if (MouseEnable) {
mEnableMouseInterface = TRUE;
}
}
if (Ps2Policy != NULL) {
Ps2Policy->Ps2InitHardware (ConsoleIn->Handle);
}
//
// Write 8042 Command Byte, set System Flag
// While at the same time:
// 1. disable mouse interface,
// 2. enable kbd interface,
// 3. enable PC/XT kbd translation mode
// 4. enable mouse and kbd interrupts
//
// ( Command Byte bits:
// 7: Reserved
// 6: PC/XT translation mode
// 5: Disable Auxiliary device interface
// 4: Disable keyboard interface
// 3: Reserved
// 2: System Flag
// 1: Enable Auxiliary device interrupt
// 0: Enable Keyboard interrupt )
//
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_WRITE);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r");
goto Done;
}
Status = KeyboardWrite (ConsoleIn, 0x67);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r");
goto Done;
}
//
// Clear Memory Scancode Buffer
//
ConsoleIn->ScancodeQueue.Head = 0;
ConsoleIn->ScancodeQueue.Tail = 0;
ConsoleIn->EfiKeyQueue.Head = 0;
ConsoleIn->EfiKeyQueue.Tail = 0;
//
// Reset the status indicators
//
ConsoleIn->LeftCtrl = FALSE;
ConsoleIn->RightCtrl = FALSE;
ConsoleIn->LeftAlt = FALSE;
ConsoleIn->RightAlt = FALSE;
ConsoleIn->LeftShift = FALSE;
ConsoleIn->RightShift = FALSE;
ConsoleIn->LeftLogo = FALSE;
ConsoleIn->RightLogo = FALSE;
ConsoleIn->Menu = FALSE;
ConsoleIn->SysReq = FALSE;
ConsoleIn->AltGrOn = FALSE;
ConsoleIn->CircumflexAccentOn = 0;
ConsoleIn->CircumflexAccentShiftedOn = 0;
ConsoleIn->GraveAccentOn = 0;
ConsoleIn->TildeOn = 0;
ConsoleIn->CurrentNsKey = NULL;
ConsoleIn->IsSupportPartialKey = FALSE;
//
// For reseting keyboard is not mandatory before booting OS and sometimes keyboard responses very slow,
// and to support KB hot plug, we need to let the InitKB succeed no matter whether there is a KB device connected
// to system. So we only do the real reseting for keyboard when user asks and there is a real KB connected t system,
// and normally during booting an OS, it's skipped.
//
if (ExtendedVerification && CheckKeyboardConnect (ConsoleIn)) {
//
// Additional verifications for keyboard interface
//
//
// Keyboard Interface Test
//
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_KEYBOARD_INTERFACE_SELF_TEST);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r");
goto Done;
}
Status = KeyboardWaitForValue (ConsoleIn, 0x00);
if (EFI_ERROR (Status)) {
KeyboardError (
ConsoleIn,
L"Some specific value not aquired from 8042 controller!\n\r"
);
goto Done;
}
//
// Keyboard reset with a BAT(Basic Assurance Test)
//
Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_RESET);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r");
goto Done;
}
Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r");
goto Done;
}
//
// wait for BAT completion code
//
mWaitForValueTimeOut = KEYBOARD_BAT_TIMEOUT;
Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_BAT_SUCCESS);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"Keyboard self test failed!\n\r");
goto Done;
}
mWaitForValueTimeOut = KEYBOARD_WAITFORVALUE_TIMEOUT;
//
// Set Keyboard to use Scan Code Set 2
//
Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_SELECT_SCAN_CODE_SET);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r");
goto Done;
}
Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r");
goto Done;
}
Status = KeyboardWrite (ConsoleIn, 0x02);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller data write error!!\n\r");
goto Done;
}
Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r");
goto Done;
}
//
// Clear Keyboard Scancode Buffer
//
Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_CLEAR_OUTPUT_DATA);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r");
goto Done;
}
Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r");
goto Done;
}
}
//
// Update Keyboard Lights
//
Status = UpdateStatusLights (ConsoleIn);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"Update keyboard status lights error!\n\r");
goto Done;
}
//
// At last, we can now enable the mouse interface if appropriate
//
Done:
if (mEnableMouseInterface) {
//
// Enable mouse interface
//
Status1 = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_ENABLE_MOUSE_INTERFACE);
if (EFI_ERROR (Status1)) {
KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r");
return EFI_DEVICE_ERROR;
}
}
if (!EFI_ERROR (Status)) {
return EFI_SUCCESS;
} else {
return EFI_DEVICE_ERROR;
}
}
/**
Disable the keyboard interface of the 8042 controller.
@param ConsoleIn The device instance
@return status of issuing disable command
**/
EFI_STATUS
DisableKeyboard (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
EFI_STATUS Status;
//
// Disable keyboard interface
//
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_DISABLE_KEYBOARD_INTERFACE);
if (EFI_ERROR (Status)) {
KeyboardError (ConsoleIn, L"\n\r");
return EFI_DEVICE_ERROR;
}
return Status;
}
/**
Check whether there is Ps/2 Keyboard device in system by 0xF4 Keyboard Command
If Keyboard receives 0xF4, it will respond with 'ACK'. If it doesn't respond, the device
should not be in system.
@param[in] ConsoleIn Keyboard Private Data Structure
@retval TRUE Keyboard in System.
@retval FALSE Keyboard not in System.
**/
BOOLEAN
EFIAPI
CheckKeyboardConnect (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
EFI_STATUS Status;
UINTN WaitForValueTimeOutBcakup;
UINT8 Values[2];
UINT8 Value;
Status = EFI_SUCCESS;
//
// clear previous data
//
while (KeyReadStatusRegister (ConsoleIn) & KBC_STSREG_VIA64_OUTB) {
KeyReadDataRegister (ConsoleIn);
gBS->Stall (15);
}
//
// enable keyboard itself and wait for its ack
// If can't receive ack, Keyboard should not be connected.
//
if (!PcdGetBool (PcdFastPS2Detection)) {
Status = KeyboardWrite (
ConsoleIn,
KEYBOARD_KBEN
);
if (EFI_ERROR (Status)) {
return FALSE;
}
//
// wait for 1s
//
WaitForValueTimeOutBcakup = mWaitForValueTimeOut;
mWaitForValueTimeOut = KEYBOARD_WAITFORVALUE_TIMEOUT;
//
// Also try to check failed ACK value to reduce waiting time.
//
Value = 0;
Values[0] = KEYBOARD_8048_RETURN_8042_ACK;
Values[1] = KEYBOARD_8048_RETURN_8042_FAILED_ACK;
Status = KeyboardWaitForValues (
ConsoleIn,
Values,
sizeof (Values),
&Value
);
mWaitForValueTimeOut = WaitForValueTimeOutBcakup;
if (EFI_ERROR (Status) || Value == KEYBOARD_8048_RETURN_8042_FAILED_ACK) {
return FALSE;
}
return TRUE;
} else {
return TRUE;
}
}
/**
Routine Description:
Check whether Ps/2 Keyboard device ready
Arguments:
ConsoleIn - the device instance
Returns:
EFI_DEVICE_ERROR - need to reset Keyboard.
EFI_SUCCESS - Keyboard ready
**/
EFI_STATUS
EFIAPI
CheckKeyboardStatus (
IN OUT KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
EFI_STATUS Status;
UINT8 Values[2];
UINT8 Value;
Status = EFI_DEVICE_ERROR;
//
// First do keyboard reset sequence, if reset cmd success during PEI phase.
//
if (KeyReadStatusRegister (ConsoleIn) & KBC_STSREG_VIA64_OUTB) {
//
// KBC should return 0xAA to indicate BAT test successful. Some KBC will return
// KBC self test OK to indicate test successful and doesn't plug-in keyboard.
//
Value = 0;
Values[0] = KEYBOARD_8048_RETURN_8042_BAT_SUCCESS;
Values[1] = KEYBOARD_8048_RETURN_8042_KBC_SELT_TEST_SUCCESS;
Status = KeyboardWaitForValues (ConsoleIn, Values, sizeof (Values), &Value);
if (!EFI_ERROR (Status) && Value == KEYBOARD_8048_RETURN_8042_BAT_SUCCESS) {
//
// Set keyboard scan code set = 02 (standard configuration)
//
Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_SELECT_SCAN_CODE_SET);
//
// Some KBC doesn't support this command and may return failed ACK data.
//
Values[0] = KEYBOARD_8048_RETURN_8042_ACK;
Values[1] = KEYBOARD_8048_RETURN_8042_FAILED_ACK;
Status = KeyboardWaitForValues (ConsoleIn, Values, sizeof (Values), &Value);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
if (Value == KEYBOARD_8048_RETURN_8042_ACK) {
Status = KeyboardWrite (ConsoleIn, 0x02);
Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
}
//
// Enable keyboard itself (not via KBC) by writing CMD F4 via 60H
//
Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_CLEAR_OUTPUT_DATA);
Value = 0;
Status = KeyboardWaitForValues (ConsoleIn, Values, sizeof (Values), &Value);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
if (Value == KEYBOARD_8048_RETURN_8042_FAILED_ACK) {
goto Exit;
}
//
// Perform KB checking by writing ABh via 64H
//
Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_KEYBOARD_INTERFACE_SELF_TEST);
Status = KeyboardWaitForValue (ConsoleIn, 0x00);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
return EFI_SUCCESS;
}
}
Exit:
return Status;
}
/**
Timer handler for Repeat Key timer.
PS/2 and USB keyboard LEDs are synchronal.
@param[in] ConsoleIn Keyboard Private Data Structure
**/
VOID
Ps2KeyboardCheckLEDHandler (
IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn
)
{
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *ConSplitterEx;
EFI_KEY_TOGGLE_STATE KeyToggleState;
EFI_STATUS Status;
KeyToggleState = ConsoleIn->IsSupportPartialKey ? EFI_TOGGLE_STATE_VALID | EFI_KEY_STATE_EXPOSED : EFI_TOGGLE_STATE_VALID;
if (ConsoleIn->CapsLock) {
KeyToggleState |= EFI_CAPS_LOCK_ACTIVE;
}
if (ConsoleIn->NumLock) {
KeyToggleState |= EFI_NUM_LOCK_ACTIVE;
}
if (ConsoleIn->ScrollLock) {
KeyToggleState |= EFI_SCROLL_LOCK_ACTIVE;
}
if (gST->ConsoleInHandle != NULL) {
Status = gBS->HandleProtocol (
gST->ConsoleInHandle,
&gEfiSimpleTextInputExProtocolGuid,
(VOID **) &ConSplitterEx
);
if (!EFI_ERROR (Status)) {
//
// Update all of ConInEx device State.
//
Status = ConSplitterEx->SetState (ConSplitterEx, &KeyToggleState);
if (Status == EFI_SUCCESS) return;
}
}
//
// Update Ps2Keyboard State.
//
ConsoleIn->ConInEx.SetState (&ConsoleIn->ConInEx, &KeyToggleState);
return;
}