/** @file | |
Routines that access 8042 keyboard controller | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Ps2Keyboard.h" | |
struct { | |
UINT8 ScanCode; ///< follows value defined in Scan Code Set1 | |
UINT16 EfiScanCode; | |
CHAR16 UnicodeChar; | |
CHAR16 ShiftUnicodeChar; | |
} ConvertKeyboardScanCodeToEfiKey[] = { | |
{ | |
0x01, // Escape | |
SCAN_ESC, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x02, | |
SCAN_NULL, | |
L'1', | |
L'!' | |
}, | |
{ | |
0x03, | |
SCAN_NULL, | |
L'2', | |
L'@' | |
}, | |
{ | |
0x04, | |
SCAN_NULL, | |
L'3', | |
L'#' | |
}, | |
{ | |
0x05, | |
SCAN_NULL, | |
L'4', | |
L'$' | |
}, | |
{ | |
0x06, | |
SCAN_NULL, | |
L'5', | |
L'%' | |
}, | |
{ | |
0x07, | |
SCAN_NULL, | |
L'6', | |
L'^' | |
}, | |
{ | |
0x08, | |
SCAN_NULL, | |
L'7', | |
L'&' | |
}, | |
{ | |
0x09, | |
SCAN_NULL, | |
L'8', | |
L'*' | |
}, | |
{ | |
0x0A, | |
SCAN_NULL, | |
L'9', | |
L'(' | |
}, | |
{ | |
0x0B, | |
SCAN_NULL, | |
L'0', | |
L')' | |
}, | |
{ | |
0x0C, | |
SCAN_NULL, | |
L'-', | |
L'_' | |
}, | |
{ | |
0x0D, | |
SCAN_NULL, | |
L'=', | |
L'+' | |
}, | |
{ | |
0x0E, // BackSpace | |
SCAN_NULL, | |
0x0008, | |
0x0008 | |
}, | |
{ | |
0x0F, // Tab | |
SCAN_NULL, | |
0x0009, | |
0x0009 | |
}, | |
{ | |
0x10, | |
SCAN_NULL, | |
L'q', | |
L'Q' | |
}, | |
{ | |
0x11, | |
SCAN_NULL, | |
L'w', | |
L'W' | |
}, | |
{ | |
0x12, | |
SCAN_NULL, | |
L'e', | |
L'E' | |
}, | |
{ | |
0x13, | |
SCAN_NULL, | |
L'r', | |
L'R' | |
}, | |
{ | |
0x14, | |
SCAN_NULL, | |
L't', | |
L'T' | |
}, | |
{ | |
0x15, | |
SCAN_NULL, | |
L'y', | |
L'Y' | |
}, | |
{ | |
0x16, | |
SCAN_NULL, | |
L'u', | |
L'U' | |
}, | |
{ | |
0x17, | |
SCAN_NULL, | |
L'i', | |
L'I' | |
}, | |
{ | |
0x18, | |
SCAN_NULL, | |
L'o', | |
L'O' | |
}, | |
{ | |
0x19, | |
SCAN_NULL, | |
L'p', | |
L'P' | |
}, | |
{ | |
0x1a, | |
SCAN_NULL, | |
L'[', | |
L'{' | |
}, | |
{ | |
0x1b, | |
SCAN_NULL, | |
L']', | |
L'}' | |
}, | |
{ | |
0x1c, // Enter | |
SCAN_NULL, | |
0x000d, | |
0x000d | |
}, | |
{ | |
0x1d, | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x1e, | |
SCAN_NULL, | |
L'a', | |
L'A' | |
}, | |
{ | |
0x1f, | |
SCAN_NULL, | |
L's', | |
L'S' | |
}, | |
{ | |
0x20, | |
SCAN_NULL, | |
L'd', | |
L'D' | |
}, | |
{ | |
0x21, | |
SCAN_NULL, | |
L'f', | |
L'F' | |
}, | |
{ | |
0x22, | |
SCAN_NULL, | |
L'g', | |
L'G' | |
}, | |
{ | |
0x23, | |
SCAN_NULL, | |
L'h', | |
L'H' | |
}, | |
{ | |
0x24, | |
SCAN_NULL, | |
L'j', | |
L'J' | |
}, | |
{ | |
0x25, | |
SCAN_NULL, | |
L'k', | |
L'K' | |
}, | |
{ | |
0x26, | |
SCAN_NULL, | |
L'l', | |
L'L' | |
}, | |
{ | |
0x27, | |
SCAN_NULL, | |
L';', | |
L':' | |
}, | |
{ | |
0x28, | |
SCAN_NULL, | |
L'\'', | |
L'"' | |
}, | |
{ | |
0x29, | |
SCAN_NULL, | |
L'`', | |
L'~' | |
}, | |
{ | |
0x2a, // Left Shift | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x2b, | |
SCAN_NULL, | |
L'\\', | |
L'|' | |
}, | |
{ | |
0x2c, | |
SCAN_NULL, | |
L'z', | |
L'Z' | |
}, | |
{ | |
0x2d, | |
SCAN_NULL, | |
L'x', | |
L'X' | |
}, | |
{ | |
0x2e, | |
SCAN_NULL, | |
L'c', | |
L'C' | |
}, | |
{ | |
0x2f, | |
SCAN_NULL, | |
L'v', | |
L'V' | |
}, | |
{ | |
0x30, | |
SCAN_NULL, | |
L'b', | |
L'B' | |
}, | |
{ | |
0x31, | |
SCAN_NULL, | |
L'n', | |
L'N' | |
}, | |
{ | |
0x32, | |
SCAN_NULL, | |
L'm', | |
L'M' | |
}, | |
{ | |
0x33, | |
SCAN_NULL, | |
L',', | |
L'<' | |
}, | |
{ | |
0x34, | |
SCAN_NULL, | |
L'.', | |
L'>' | |
}, | |
{ | |
0x35, | |
SCAN_NULL, | |
L'/', | |
L'?' | |
}, | |
{ | |
0x36, // Right Shift | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x37, // Numeric Keypad * | |
SCAN_NULL, | |
L'*', | |
L'*' | |
}, | |
{ | |
0x38, // Left Alt/Extended Right Alt | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x39, | |
SCAN_NULL, | |
L' ', | |
L' ' | |
}, | |
{ | |
0x3A, // CapsLock | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x3B, | |
SCAN_F1, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x3C, | |
SCAN_F2, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x3D, | |
SCAN_F3, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x3E, | |
SCAN_F4, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x3F, | |
SCAN_F5, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x40, | |
SCAN_F6, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x41, | |
SCAN_F7, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x42, | |
SCAN_F8, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x43, | |
SCAN_F9, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x44, | |
SCAN_F10, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x45, // NumLock | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x46, // ScrollLock | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x47, | |
SCAN_HOME, | |
L'7', | |
L'7' | |
}, | |
{ | |
0x48, | |
SCAN_UP, | |
L'8', | |
L'8' | |
}, | |
{ | |
0x49, | |
SCAN_PAGE_UP, | |
L'9', | |
L'9' | |
}, | |
{ | |
0x4a, | |
SCAN_NULL, | |
L'-', | |
L'-' | |
}, | |
{ | |
0x4b, | |
SCAN_LEFT, | |
L'4', | |
L'4' | |
}, | |
{ | |
0x4c, // Numeric Keypad 5 | |
SCAN_NULL, | |
L'5', | |
L'5' | |
}, | |
{ | |
0x4d, | |
SCAN_RIGHT, | |
L'6', | |
L'6' | |
}, | |
{ | |
0x4e, | |
SCAN_NULL, | |
L'+', | |
L'+' | |
}, | |
{ | |
0x4f, | |
SCAN_END, | |
L'1', | |
L'1' | |
}, | |
{ | |
0x50, | |
SCAN_DOWN, | |
L'2', | |
L'2' | |
}, | |
{ | |
0x51, | |
SCAN_PAGE_DOWN, | |
L'3', | |
L'3' | |
}, | |
{ | |
0x52, | |
SCAN_INSERT, | |
L'0', | |
L'0' | |
}, | |
{ | |
0x53, | |
SCAN_DELETE, | |
L'.', | |
L'.' | |
}, | |
{ | |
0x57, | |
SCAN_F11, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x58, | |
SCAN_F12, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x5B, // Left LOGO | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x5C, // Right LOGO | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
0x5D, // Menu key | |
SCAN_NULL, | |
0x0000, | |
0x0000 | |
}, | |
{ | |
TABLE_END, | |
TABLE_END, | |
SCAN_NULL, | |
SCAN_NULL | |
}, | |
}; | |
// | |
// 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 | |
) | |
{ | |
return IoRead8 (ConsoleIn->DataRegisterAddress); | |
} | |
/** | |
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 | |
) | |
{ | |
IoWrite8 (ConsoleIn->DataRegisterAddress, 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 | |
) | |
{ | |
return IoRead8 (ConsoleIn->StatusRegisterAddress); | |
} | |
/** | |
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 | |
) | |
{ | |
IoWrite8 (ConsoleIn->CommandRegisterAddress, 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 should 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 | |
) | |
{ | |
// | |
// 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 register 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 success 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; | |
} | |
} | |
/** | |
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; | |
// | |
// Send keyboard command | |
// | |
Status = KeyboardWrite (ConsoleIn, 0xed); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
KeyboardWaitForValue (ConsoleIn, 0xfa); | |
// | |
// 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; | |
} | |
KeyboardWaitForValue (ConsoleIn, 0xfa); | |
return Status; | |
} | |
/** | |
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; | |
// | |
// Check if there are enough bytes of scancode representing a single key | |
// available in the buffer | |
// | |
while (TRUE) { | |
Extend0 = FALSE; | |
Extend1 = FALSE; | |
ScancodeArrPos = 0; | |
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) { | |
// | |
// Check for special keys and update the driver state. | |
// | |
switch (ScanCode) { | |
case SCANCODE_CTRL_MAKE: | |
if (Extend0) { | |
ConsoleIn->RightCtrl = TRUE; | |
} else { | |
ConsoleIn->LeftCtrl = TRUE; | |
} | |
break; | |
case SCANCODE_CTRL_BREAK: | |
if (Extend0) { | |
ConsoleIn->RightCtrl = FALSE; | |
} else { | |
ConsoleIn->LeftCtrl = FALSE; | |
} | |
break; | |
case SCANCODE_ALT_MAKE: | |
if (Extend0) { | |
ConsoleIn->RightAlt = TRUE; | |
} else { | |
ConsoleIn->LeftAlt = TRUE; | |
} | |
break; | |
case SCANCODE_ALT_BREAK: | |
if (Extend0) { | |
ConsoleIn->RightAlt = FALSE; | |
} else { | |
ConsoleIn->LeftAlt = FALSE; | |
} | |
break; | |
case SCANCODE_LEFT_SHIFT_MAKE: | |
// | |
// 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. | |
// | |
if (!Extend0) { | |
ConsoleIn->LeftShift = TRUE; | |
break; | |
} | |
continue; | |
case SCANCODE_LEFT_SHIFT_BREAK: | |
if (!Extend0) { | |
ConsoleIn->LeftShift = FALSE; | |
} | |
break; | |
case SCANCODE_RIGHT_SHIFT_MAKE: | |
ConsoleIn->RightShift = TRUE; | |
break; | |
case SCANCODE_RIGHT_SHIFT_BREAK: | |
ConsoleIn->RightShift = FALSE; | |
break; | |
case SCANCODE_LEFT_LOGO_MAKE: | |
ConsoleIn->LeftLogo = TRUE; | |
break; | |
case SCANCODE_LEFT_LOGO_BREAK: | |
ConsoleIn->LeftLogo = FALSE; | |
break; | |
case SCANCODE_RIGHT_LOGO_MAKE: | |
ConsoleIn->RightLogo = TRUE; | |
break; | |
case SCANCODE_RIGHT_LOGO_BREAK: | |
ConsoleIn->RightLogo = FALSE; | |
break; | |
case SCANCODE_MENU_MAKE: | |
ConsoleIn->Menu = TRUE; | |
break; | |
case SCANCODE_MENU_BREAK: | |
ConsoleIn->Menu = FALSE; | |
break; | |
case SCANCODE_SYS_REQ_MAKE: | |
if (Extend0) { | |
ConsoleIn->SysReq = TRUE; | |
} | |
break; | |
case SCANCODE_SYS_REQ_BREAK: | |
if (Extend0) { | |
ConsoleIn->SysReq = FALSE; | |
} | |
break; | |
case SCANCODE_SYS_REQ_MAKE_WITH_ALT: | |
ConsoleIn->SysReq = TRUE; | |
break; | |
case SCANCODE_SYS_REQ_BREAK_WITH_ALT: | |
ConsoleIn->SysReq = FALSE; | |
break; | |
case SCANCODE_CAPS_LOCK_MAKE: | |
ConsoleIn->CapsLock = (BOOLEAN) !ConsoleIn->CapsLock; | |
UpdateStatusLights (ConsoleIn); | |
break; | |
case SCANCODE_NUM_LOCK_MAKE: | |
ConsoleIn->NumLock = (BOOLEAN) !ConsoleIn->NumLock; | |
UpdateStatusLights (ConsoleIn); | |
break; | |
case SCANCODE_SCROLL_LOCK_MAKE: | |
if (!Extend0) { | |
ConsoleIn->ScrollLock = (BOOLEAN) !ConsoleIn->ScrollLock; | |
UpdateStatusLights (ConsoleIn); | |
} | |
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) && | |
(ScanCode == SCANCODE_DELETE_MAKE) | |
) | |
{ | |
gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); | |
} | |
// | |
// Save the Shift/Toggle state | |
// | |
InitializeKeyState (ConsoleIn, &KeyData.KeyState); | |
KeyData.Key.ScanCode = SCAN_NULL; | |
KeyData.Key.UnicodeChar = CHAR_NULL; | |
// | |
// Key Pad "/" shares the same scancode as that of "/" except Key Pad "/" has E0 prefix | |
// | |
if (Extend0 && (ScanCode == 0x35)) { | |
KeyData.Key.UnicodeChar = L'/'; | |
KeyData.Key.ScanCode = SCAN_NULL; | |
// | |
// PAUSE shares the same scancode as that of NUM except PAUSE has E1 prefix | |
// | |
} else if (Extend1 && (ScanCode == SCANCODE_NUM_LOCK_MAKE)) { | |
KeyData.Key.UnicodeChar = CHAR_NULL; | |
KeyData.Key.ScanCode = SCAN_PAUSE; | |
// | |
// PAUSE shares the same scancode as that of SCROLL except PAUSE (CTRL pressed) has E0 prefix | |
// | |
} else if (Extend0 && (ScanCode == SCANCODE_SCROLL_LOCK_MAKE)) { | |
KeyData.Key.UnicodeChar = CHAR_NULL; | |
KeyData.Key.ScanCode = SCAN_PAUSE; | |
// | |
// PRNT_SCRN shares the same scancode as that of Key Pad "*" except PRNT_SCRN has E0 prefix | |
// | |
} else if (Extend0 && (ScanCode == SCANCODE_SYS_REQ_MAKE)) { | |
KeyData.Key.UnicodeChar = CHAR_NULL; | |
KeyData.Key.ScanCode = SCAN_NULL; | |
// | |
// Except the above special case, all others can be handled by convert table | |
// | |
} else { | |
for (Index = 0; ConvertKeyboardScanCodeToEfiKey[Index].ScanCode != TABLE_END; Index++) { | |
if (ScanCode == ConvertKeyboardScanCodeToEfiKey[Index].ScanCode) { | |
KeyData.Key.ScanCode = ConvertKeyboardScanCodeToEfiKey[Index].EfiScanCode; | |
KeyData.Key.UnicodeChar = ConvertKeyboardScanCodeToEfiKey[Index].UnicodeChar; | |
if ((ConsoleIn->LeftShift || ConsoleIn->RightShift) && | |
(ConvertKeyboardScanCodeToEfiKey[Index].UnicodeChar != ConvertKeyboardScanCodeToEfiKey[Index].ShiftUnicodeChar)) | |
{ | |
KeyData.Key.UnicodeChar = ConvertKeyboardScanCodeToEfiKey[Index].ShiftUnicodeChar; | |
// | |
// 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' | |
// | |
KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED); | |
} | |
// | |
// alphabetic key is affected by CapsLock State | |
// | |
if (ConsoleIn->CapsLock) { | |
if ((KeyData.Key.UnicodeChar >= L'a') && (KeyData.Key.UnicodeChar <= L'z')) { | |
KeyData.Key.UnicodeChar = (UINT16)(KeyData.Key.UnicodeChar - L'a' + L'A'); | |
} else if ((KeyData.Key.UnicodeChar >= L'A') && (KeyData.Key.UnicodeChar <= L'Z')) { | |
KeyData.Key.UnicodeChar = (UINT16)(KeyData.Key.UnicodeChar - L'A' + L'a'); | |
} | |
} | |
break; | |
} | |
} | |
} | |
// | |
// distinguish numeric key pad keys' 'up symbol' and 'down symbol' | |
// | |
if ((ScanCode >= 0x47) && (ScanCode <= 0x53)) { | |
if (ConsoleIn->NumLock && !(ConsoleIn->LeftShift || ConsoleIn->RightShift) && !Extend0) { | |
KeyData.Key.ScanCode = SCAN_NULL; | |
} else if ((ScanCode != 0x4a) && (ScanCode != 0x4e)) { | |
KeyData.Key.UnicodeChar = CHAR_NULL; | |
} | |
} | |
// | |
// 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; | |
} | |
} | |
// | |
// Signal KeyNotify process event if this key pressed matches any key registered. | |
// | |
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)) { | |
// | |
// The key notification function needs to run at TPL_CALLBACK | |
// while current TPL is TPL_NOTIFY. It will be invoked in | |
// KeyNotifyProcessHandler() which runs at TPL_CALLBACK. | |
// | |
PushEfikeyBufTail (&ConsoleIn->EfiKeyQueueForNotify, &KeyData); | |
gBS->SignalEvent (ConsoleIn->KeyNotifyProcessEvent); | |
break; | |
} | |
} | |
PushEfikeyBufTail (&ConsoleIn->EfiKeyQueue, &KeyData); | |
} | |
/** | |
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; | |
Status = EFI_SUCCESS; | |
mEnableMouseInterface = TRUE; | |
TryTime = 0; | |
// | |
// Get Ps2 policy to set this | |
// | |
gBS->LocateProtocol ( | |
&gEfiPs2PolicyProtocolGuid, | |
NULL, | |
(VOID **)&Ps2Policy | |
); | |
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) != 0) { | |
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 | |
// | |
if (!PcdGetBool (PcdFastPS2Detection)) { | |
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 (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; | |
ConsoleIn->EfiKeyQueueForNotify.Head = 0; | |
ConsoleIn->EfiKeyQueueForNotify.Tail = 0; | |
// | |
// Reset the status indicators | |
// | |
ConsoleIn->CapsLock = FALSE; | |
ConsoleIn->NumLock = FALSE; | |
ConsoleIn->ScrollLock = FALSE; | |
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->IsSupportPartialKey = FALSE; | |
// | |
// For resetting 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 resetting 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 acquired 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 acquired from 8042 controller!\n\r"); | |
goto Done; | |
} | |
// | |
// wait for BAT completion code | |
// | |
KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_BAT_SUCCESS); | |
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 acquired 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 acquired 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 acquired from 8042 controller!\n\r"); | |
goto Done; | |
} | |
// | |
if (Ps2Policy != NULL) { | |
if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_CAPSLOCK) == EFI_KEYBOARD_CAPSLOCK) { | |
ConsoleIn->CapsLock = TRUE; | |
} | |
if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_NUMLOCK) == EFI_KEYBOARD_NUMLOCK) { | |
ConsoleIn->NumLock = TRUE; | |
} | |
if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_SCROLLLOCK) == EFI_KEYBOARD_SCROLLLOCK) { | |
ConsoleIn->ScrollLock = TRUE; | |
} | |
} | |
// | |
// 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; | |
} | |
} | |
/** | |
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; | |
// | |
// 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; | |
Status = KeyboardWaitForValue ( | |
ConsoleIn, | |
KEYBOARD_CMDECHO_ACK | |
); | |
mWaitForValueTimeOut = WaitForValueTimeOutBcakup; | |
if (EFI_ERROR (Status)) { | |
return FALSE; | |
} | |
return TRUE; | |
} else { | |
return TRUE; | |
} | |
} |