| /** @file | |
| ConsoleOut Routines that speak VGA. | |
| Copyright (c) 2006 - 2017, 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 "BiosKeyboard.h" | |
| // | |
| // EFI Driver Binding Protocol Instance | |
| // | |
| EFI_DRIVER_BINDING_PROTOCOL gBiosKeyboardDriverBinding = { | |
| BiosKeyboardDriverBindingSupported, | |
| BiosKeyboardDriverBindingStart, | |
| BiosKeyboardDriverBindingStop, | |
| 0x3, | |
| NULL, | |
| NULL | |
| }; | |
| /** | |
| Enqueue the key. | |
| @param Queue The queue to be enqueued. | |
| @param KeyData The key data to be enqueued. | |
| @retval EFI_NOT_READY The queue is full. | |
| @retval EFI_SUCCESS Successfully enqueued the key data. | |
| **/ | |
| EFI_STATUS | |
| Enqueue ( | |
| IN SIMPLE_QUEUE *Queue, | |
| IN EFI_KEY_DATA *KeyData | |
| ) | |
| { | |
| if ((Queue->Rear + 1) % QUEUE_MAX_COUNT == Queue->Front) { | |
| return EFI_NOT_READY; | |
| } | |
| CopyMem (&Queue->Buffer[Queue->Rear], KeyData, sizeof (EFI_KEY_DATA)); | |
| Queue->Rear = (Queue->Rear + 1) % QUEUE_MAX_COUNT; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Dequeue the key. | |
| @param Queue The queue to be dequeued. | |
| @param KeyData The key data to be dequeued. | |
| @retval EFI_NOT_READY The queue is empty. | |
| @retval EFI_SUCCESS Successfully dequeued the key data. | |
| **/ | |
| EFI_STATUS | |
| Dequeue ( | |
| IN SIMPLE_QUEUE *Queue, | |
| IN EFI_KEY_DATA *KeyData | |
| ) | |
| { | |
| if (Queue->Front == Queue->Rear) { | |
| return EFI_NOT_READY; | |
| } | |
| CopyMem (KeyData, &Queue->Buffer[Queue->Front], sizeof (EFI_KEY_DATA)); | |
| Queue->Front = (Queue->Front + 1) % QUEUE_MAX_COUNT; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether the queue is empty. | |
| @param Queue The queue to be checked. | |
| @retval EFI_NOT_READY The queue is empty. | |
| @retval EFI_SUCCESS The queue is not empty. | |
| **/ | |
| EFI_STATUS | |
| CheckQueue ( | |
| IN SIMPLE_QUEUE *Queue | |
| ) | |
| { | |
| if (Queue->Front == Queue->Rear) { | |
| return EFI_NOT_READY; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // EFI Driver Binding Protocol Functions | |
| // | |
| /** | |
| Check whether the driver supports this device. | |
| @param This The Udriver binding protocol. | |
| @param Controller The controller handle to check. | |
| @param RemainingDevicePath The remaining device path. | |
| @retval EFI_SUCCESS The driver supports this controller. | |
| @retval other This device isn't supported. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardDriverBindingSupported ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| EFI_ISA_IO_PROTOCOL *IsaIo; | |
| // | |
| // See if the Legacy BIOS Protocol is available | |
| // | |
| Status = gBS->LocateProtocol ( | |
| &gEfiLegacyBiosProtocolGuid, | |
| NULL, | |
| (VOID **) &LegacyBios | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Open the IO Abstraction(s) needed to perform the supported test | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiIsaIoProtocolGuid, | |
| (VOID **) &IsaIo, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Use the ISA I/O Protocol to see if Controller is the Keyboard controller | |
| // | |
| if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x303) || IsaIo->ResourceList->Device.UID != 0) { | |
| Status = EFI_UNSUPPORTED; | |
| } | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiIsaIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Starts the device with this driver. | |
| @param This The driver binding instance. | |
| @param Controller Handle of device to bind driver to. | |
| @param RemainingDevicePath Optional parameter use to pick a specific child | |
| device to start. | |
| @retval EFI_SUCCESS The controller is controlled by the driver. | |
| @retval Other This controller cannot be started. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardDriverBindingStart ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| EFI_ISA_IO_PROTOCOL *IsaIo; | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_IA32_REGISTER_SET Regs; | |
| BOOLEAN CarryFlag; | |
| EFI_PS2_POLICY_PROTOCOL *Ps2Policy; | |
| UINT8 Command; | |
| EFI_STATUS_CODE_VALUE StatusCode; | |
| BiosKeyboardPrivate = NULL; | |
| IsaIo = NULL; | |
| StatusCode = 0; | |
| // | |
| // Get Ps2 policy to set. Will be use if present. | |
| // | |
| gBS->LocateProtocol ( | |
| &gEfiPs2PolicyProtocolGuid, | |
| NULL, | |
| (VOID **) &Ps2Policy | |
| ); | |
| // | |
| // See if the Legacy BIOS Protocol is available | |
| // | |
| Status = gBS->LocateProtocol ( | |
| &gEfiLegacyBiosProtocolGuid, | |
| NULL, | |
| (VOID **) &LegacyBios | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Open the IO Abstraction(s) needed | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiIsaIoProtocolGuid, | |
| (VOID **) &IsaIo, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Allocate the private device structure | |
| // | |
| BiosKeyboardPrivate = (BIOS_KEYBOARD_DEV *) AllocateZeroPool (sizeof (BIOS_KEYBOARD_DEV)); | |
| if (NULL == BiosKeyboardPrivate) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Done; | |
| } | |
| // | |
| // Initialize the private device structure | |
| // | |
| BiosKeyboardPrivate->Signature = BIOS_KEYBOARD_DEV_SIGNATURE; | |
| BiosKeyboardPrivate->Handle = Controller; | |
| BiosKeyboardPrivate->LegacyBios = LegacyBios; | |
| BiosKeyboardPrivate->IsaIo = IsaIo; | |
| BiosKeyboardPrivate->SimpleTextIn.Reset = BiosKeyboardReset; | |
| BiosKeyboardPrivate->SimpleTextIn.ReadKeyStroke = BiosKeyboardReadKeyStroke; | |
| BiosKeyboardPrivate->DataRegisterAddress = KEYBOARD_8042_DATA_REGISTER; | |
| BiosKeyboardPrivate->StatusRegisterAddress = KEYBOARD_8042_STATUS_REGISTER; | |
| BiosKeyboardPrivate->CommandRegisterAddress = KEYBOARD_8042_COMMAND_REGISTER; | |
| BiosKeyboardPrivate->ExtendedKeyboard = TRUE; | |
| BiosKeyboardPrivate->Queue.Front = 0; | |
| BiosKeyboardPrivate->Queue.Rear = 0; | |
| BiosKeyboardPrivate->QueueForNotify.Front = 0; | |
| BiosKeyboardPrivate->QueueForNotify.Rear = 0; | |
| BiosKeyboardPrivate->SimpleTextInputEx.Reset = BiosKeyboardResetEx; | |
| BiosKeyboardPrivate->SimpleTextInputEx.ReadKeyStrokeEx = BiosKeyboardReadKeyStrokeEx; | |
| BiosKeyboardPrivate->SimpleTextInputEx.SetState = BiosKeyboardSetState; | |
| BiosKeyboardPrivate->SimpleTextInputEx.RegisterKeyNotify = BiosKeyboardRegisterKeyNotify; | |
| BiosKeyboardPrivate->SimpleTextInputEx.UnregisterKeyNotify = BiosKeyboardUnregisterKeyNotify; | |
| InitializeListHead (&BiosKeyboardPrivate->NotifyList); | |
| // | |
| // Report that the keyboard is being enabled | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_PROGRESS_CODE, | |
| EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE | |
| ); | |
| // | |
| // Setup the WaitForKey event | |
| // | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_WAIT, | |
| TPL_NOTIFY, | |
| BiosKeyboardWaitForKey, | |
| &(BiosKeyboardPrivate->SimpleTextIn), | |
| &((BiosKeyboardPrivate->SimpleTextIn).WaitForKey) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| (BiosKeyboardPrivate->SimpleTextIn).WaitForKey = NULL; | |
| goto Done; | |
| } | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_WAIT, | |
| TPL_NOTIFY, | |
| BiosKeyboardWaitForKeyEx, | |
| &(BiosKeyboardPrivate->SimpleTextInputEx), | |
| &(BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx = NULL; | |
| goto Done; | |
| } | |
| // | |
| // Setup a periodic timer, used for reading keystrokes at a fixed interval | |
| // | |
| Status = gBS->CreateEvent ( | |
| EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
| TPL_NOTIFY, | |
| BiosKeyboardTimerHandler, | |
| BiosKeyboardPrivate, | |
| &BiosKeyboardPrivate->TimerEvent | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; | |
| goto Done; | |
| } | |
| Status = gBS->SetTimer ( | |
| BiosKeyboardPrivate->TimerEvent, | |
| TimerPeriodic, | |
| KEYBOARD_TIMER_INTERVAL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; | |
| goto Done; | |
| } | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| KeyNotifyProcessHandler, | |
| BiosKeyboardPrivate, | |
| &BiosKeyboardPrivate->KeyNotifyProcessEvent | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; | |
| goto Done; | |
| } | |
| // | |
| // Report a Progress Code for an attempt to detect the precense of the keyboard device in the system | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_PROGRESS_CODE, | |
| EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT | |
| ); | |
| // | |
| // Reset the keyboard device | |
| // | |
| Status = BiosKeyboardPrivate->SimpleTextInputEx.Reset ( | |
| &BiosKeyboardPrivate->SimpleTextInputEx, | |
| FeaturePcdGet (PcdPs2KbdExtendedVerification) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_ERROR, "[KBD]Reset Failed. Status - %r\n", Status)); | |
| StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED; | |
| goto Done; | |
| } | |
| // | |
| // Do platform specific policy like port swapping and keyboard light default | |
| // | |
| if (Ps2Policy != NULL) { | |
| Ps2Policy->Ps2InitHardware (Controller); | |
| Command = 0; | |
| if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_CAPSLOCK) == EFI_KEYBOARD_CAPSLOCK) { | |
| Command |= 4; | |
| } | |
| if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_NUMLOCK) == EFI_KEYBOARD_NUMLOCK) { | |
| Command |= 2; | |
| } | |
| if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_SCROLLLOCK) == EFI_KEYBOARD_SCROLLLOCK) { | |
| Command |= 1; | |
| } | |
| KeyboardWrite (BiosKeyboardPrivate, 0xed); | |
| KeyboardWaitForValue (BiosKeyboardPrivate, 0xfa, KEYBOARD_WAITFORVALUE_TIMEOUT); | |
| KeyboardWrite (BiosKeyboardPrivate, Command); | |
| // | |
| // Call Legacy BIOS Protocol to set whatever is necessary | |
| // | |
| LegacyBios->UpdateKeyboardLedStatus (LegacyBios, Command); | |
| } | |
| // | |
| // Get Configuration | |
| // | |
| Regs.H.AH = 0xc0; | |
| CarryFlag = BiosKeyboardPrivate->LegacyBios->Int86 ( | |
| BiosKeyboardPrivate->LegacyBios, | |
| 0x15, | |
| &Regs | |
| ); | |
| if (!CarryFlag) { | |
| // | |
| // Check bit 6 of Feature Byte 2. | |
| // If it is set, then Int 16 Func 09 is supported | |
| // | |
| if (*(UINT8 *) (((UINTN) Regs.X.ES << 4) + Regs.X.BX + 0x06) & 0x40) { | |
| // | |
| // Get Keyboard Functionality | |
| // | |
| Regs.H.AH = 0x09; | |
| CarryFlag = BiosKeyboardPrivate->LegacyBios->Int86 ( | |
| BiosKeyboardPrivate->LegacyBios, | |
| 0x16, | |
| &Regs | |
| ); | |
| if (!CarryFlag) { | |
| // | |
| // Check bit 5 of AH. | |
| // If it is set, then INT 16 Finc 10-12 are supported. | |
| // | |
| if ((Regs.H.AL & 0x40) != 0) { | |
| // | |
| // Set the flag to use INT 16 Func 10-12 | |
| // | |
| BiosKeyboardPrivate->ExtendedKeyboard = TRUE; | |
| } | |
| } | |
| } | |
| } | |
| DEBUG ((EFI_D_INFO, "[KBD]Extended keystrokes supported by CSM16 - %02x\n", (UINTN)BiosKeyboardPrivate->ExtendedKeyboard)); | |
| // | |
| // Install protocol interfaces for the keyboard device. | |
| // | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &Controller, | |
| &gEfiSimpleTextInProtocolGuid, | |
| &BiosKeyboardPrivate->SimpleTextIn, | |
| &gEfiSimpleTextInputExProtocolGuid, | |
| &BiosKeyboardPrivate->SimpleTextInputEx, | |
| NULL | |
| ); | |
| Done: | |
| if (StatusCode != 0) { | |
| // | |
| // Report an Error Code for failing to start the keyboard device | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
| StatusCode | |
| ); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| if (BiosKeyboardPrivate != NULL) { | |
| if ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey != NULL) { | |
| gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey); | |
| } | |
| if ((BiosKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx != NULL) { | |
| gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx); | |
| } | |
| if (BiosKeyboardPrivate->KeyNotifyProcessEvent != NULL) { | |
| gBS->CloseEvent (BiosKeyboardPrivate->KeyNotifyProcessEvent); | |
| } | |
| BiosKeyboardFreeNotifyList (&BiosKeyboardPrivate->NotifyList); | |
| if (BiosKeyboardPrivate->TimerEvent != NULL) { | |
| gBS->CloseEvent (BiosKeyboardPrivate->TimerEvent); | |
| } | |
| FreePool (BiosKeyboardPrivate); | |
| } | |
| if (IsaIo != NULL) { | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiIsaIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Stop the device handled by this driver. | |
| @param This The driver binding protocol. | |
| @param Controller The controller to release. | |
| @param NumberOfChildren The number of handles in ChildHandleBuffer. | |
| @param ChildHandleBuffer The array of child handle. | |
| @retval EFI_SUCCESS The device was stopped. | |
| @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. | |
| @retval Others Fail to uninstall protocols attached on the device. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardDriverBindingStop ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN UINTN NumberOfChildren, | |
| IN EFI_HANDLE *ChildHandleBuffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn; | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| // | |
| // Disable Keyboard | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiSimpleTextInProtocolGuid, | |
| (VOID **) &SimpleTextIn, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiSimpleTextInputExProtocolGuid, | |
| NULL, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_TEST_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (SimpleTextIn); | |
| Status = gBS->UninstallMultipleProtocolInterfaces ( | |
| Controller, | |
| &gEfiSimpleTextInProtocolGuid, | |
| &BiosKeyboardPrivate->SimpleTextIn, | |
| &gEfiSimpleTextInputExProtocolGuid, | |
| &BiosKeyboardPrivate->SimpleTextInputEx, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Release the IsaIo protocol on the controller handle | |
| // | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiIsaIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| // | |
| // Free other resources | |
| // | |
| gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey); | |
| gBS->CloseEvent (BiosKeyboardPrivate->TimerEvent); | |
| gBS->CloseEvent (BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx); | |
| gBS->CloseEvent (BiosKeyboardPrivate->KeyNotifyProcessEvent); | |
| BiosKeyboardFreeNotifyList (&BiosKeyboardPrivate->NotifyList); | |
| FreePool (BiosKeyboardPrivate); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Read data byte from output buffer of Keyboard Controller without delay and waiting for buffer-empty state. | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @return The data byte read from output buffer of Keyboard Controller from data port which often is port 60H. | |
| **/ | |
| UINT8 | |
| KeyReadDataRegister ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate | |
| ) | |
| { | |
| UINT8 Data; | |
| // | |
| // Use IsaIo protocol to perform IO operations | |
| // | |
| BiosKeyboardPrivate->IsaIo->Io.Read ( | |
| BiosKeyboardPrivate->IsaIo, | |
| EfiIsaIoWidthUint8, | |
| BiosKeyboardPrivate->DataRegisterAddress, | |
| 1, | |
| &Data | |
| ); | |
| return Data; | |
| } | |
| /** | |
| Read status byte from status register of Keyboard Controller without delay and waiting for buffer-empty state. | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @return The status byte read from status register of Keyboard Controller from command port which often is port 64H. | |
| **/ | |
| UINT8 | |
| KeyReadStatusRegister ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate | |
| ) | |
| { | |
| UINT8 Data; | |
| // | |
| // Use IsaIo protocol to perform IO operations | |
| // | |
| BiosKeyboardPrivate->IsaIo->Io.Read ( | |
| BiosKeyboardPrivate->IsaIo, | |
| EfiIsaIoWidthUint8, | |
| BiosKeyboardPrivate->StatusRegisterAddress, | |
| 1, | |
| &Data | |
| ); | |
| return Data; | |
| } | |
| /** | |
| Write command byte to control register of Keyboard Controller without delay and waiting for buffer-empty state. | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @param Data Data byte to write. | |
| **/ | |
| VOID | |
| KeyWriteCommandRegister ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, | |
| IN UINT8 Data | |
| ) | |
| { | |
| // | |
| // Use IsaIo protocol to perform IO operations | |
| // | |
| BiosKeyboardPrivate->IsaIo->Io.Write ( | |
| BiosKeyboardPrivate->IsaIo, | |
| EfiIsaIoWidthUint8, | |
| BiosKeyboardPrivate->CommandRegisterAddress, | |
| 1, | |
| &Data | |
| ); | |
| } | |
| /** | |
| Write data byte to input buffer or input/output ports of Keyboard Controller without delay and waiting for buffer-empty state. | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @param Data Data byte to write. | |
| **/ | |
| VOID | |
| KeyWriteDataRegister ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, | |
| IN UINT8 Data | |
| ) | |
| { | |
| // | |
| // Use IsaIo protocol to perform IO operations | |
| // | |
| BiosKeyboardPrivate->IsaIo->Io.Write ( | |
| BiosKeyboardPrivate->IsaIo, | |
| EfiIsaIoWidthUint8, | |
| BiosKeyboardPrivate->DataRegisterAddress, | |
| 1, | |
| &Data | |
| ); | |
| } | |
| /** | |
| Read data byte from output buffer of Keyboard Controller with delay and waiting for buffer-empty state. | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @param Data The pointer for data that being read out. | |
| @retval EFI_SUCCESS The data byte read out successfully. | |
| @retval EFI_TIMEOUT Timeout occurred during reading out data byte. | |
| **/ | |
| EFI_STATUS | |
| KeyboardRead ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, | |
| 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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_OUTB) != 0) { | |
| RegFilled = 1; | |
| *Data = KeyReadDataRegister (BiosKeyboardPrivate); | |
| break; | |
| } | |
| gBS->Stall (30); | |
| } | |
| if (RegFilled == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Write data byte to input buffer or input/output ports of Keyboard Controller with delay and waiting for buffer-empty state. | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @param Data Data byte to write. | |
| @retval EFI_SUCCESS The data byte is written successfully. | |
| @retval EFI_TIMEOUT Timeout occurred during writing. | |
| **/ | |
| EFI_STATUS | |
| KeyboardWrite ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, | |
| 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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) { | |
| RegEmptied = 1; | |
| break; | |
| } | |
| gBS->Stall (30); | |
| } | |
| if (RegEmptied == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| // | |
| // Write it | |
| // | |
| KeyWriteDataRegister (BiosKeyboardPrivate, Data); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Write command byte to control register of Keyboard Controller with delay and waiting for buffer-empty state. | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @param Data Command byte to write. | |
| @retval EFI_SUCCESS The command byte is written successfully. | |
| @retval EFI_TIMEOUT Timeout occurred during writing. | |
| **/ | |
| EFI_STATUS | |
| KeyboardCommand ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, | |
| 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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) { | |
| RegEmptied = 1; | |
| break; | |
| } | |
| gBS->Stall (30); | |
| } | |
| if (RegEmptied == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| // | |
| // issue the command | |
| // | |
| KeyWriteCommandRegister (BiosKeyboardPrivate, Data); | |
| // | |
| // Wait For Input Buffer Empty again | |
| // | |
| RegEmptied = 0; | |
| for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { | |
| if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) { | |
| RegEmptied = 1; | |
| break; | |
| } | |
| gBS->Stall (30); | |
| } | |
| if (RegEmptied == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Wait for a specific value to be presented in | |
| Data register of Keyboard Controller by keyboard and then read it, | |
| used in keyboard commands ack | |
| @param BiosKeyboardPrivate Keyboard instance pointer. | |
| @param Value The value to be waited for | |
| @param WaitForValueTimeOut The limit of microseconds for timeout | |
| @retval EFI_SUCCESS The command byte is written successfully. | |
| @retval EFI_TIMEOUT Timeout occurred during writing. | |
| **/ | |
| EFI_STATUS | |
| KeyboardWaitForValue ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, | |
| IN UINT8 Value, | |
| IN UINTN WaitForValueTimeOut | |
| ) | |
| { | |
| 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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_OUTB) != 0) { | |
| Data = KeyReadDataRegister (BiosKeyboardPrivate); | |
| break; | |
| } | |
| gBS->Stall (30); | |
| } | |
| SumTimeOut += TimeOut; | |
| if (Data == Value) { | |
| GotIt = 1; | |
| break; | |
| } | |
| if (SumTimeOut >= WaitForValueTimeOut) { | |
| break; | |
| } | |
| } | |
| // | |
| // Check results | |
| // | |
| if (GotIt != 0) { | |
| return EFI_SUCCESS; | |
| } else { | |
| return EFI_TIMEOUT; | |
| } | |
| } | |
| /** | |
| Reads the next keystroke from the input device. The WaitForKey Event can | |
| be used to test for existance of a keystroke via WaitForEvent () call. | |
| @param BiosKeyboardPrivate Bioskeyboard driver private structure. | |
| @param KeyData A pointer to a buffer that is filled in with the keystroke | |
| state data for the key that was pressed. | |
| @retval EFI_SUCCESS The keystroke information was returned. | |
| @retval EFI_NOT_READY There was no keystroke data availiable. | |
| @retval EFI_DEVICE_ERROR The keystroke information was not returned due to | |
| hardware errors. | |
| @retval EFI_INVALID_PARAMETER KeyData is NULL. | |
| **/ | |
| EFI_STATUS | |
| KeyboardReadKeyStrokeWorker ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, | |
| OUT EFI_KEY_DATA *KeyData | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_TPL OldTpl; | |
| if (KeyData == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Use TimerEvent callback function to check whether there's any key pressed | |
| // | |
| // | |
| // Stall 1ms to give a chance to let other driver interrupt this routine for their timer event. | |
| // Csm will be used to check whether there is a key pending, but the csm will disable all | |
| // interrupt before switch to compatibility16, which mean all the efiCompatibility timer | |
| // event will stop work during the compatibility16. And If a caller recursivly invoke this function, | |
| // e.g. OS loader, other drivers which are driven by timer event will have a bad performance during this period, | |
| // e.g. usb keyboard driver. | |
| // Add a stall period can greatly increate other driver performance during the WaitForKey is recursivly invoked. | |
| // 1ms delay will make little impact to the thunk keyboard driver, and user can not feel the delay at all when input. | |
| // | |
| gBS->Stall (1000); | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| BiosKeyboardTimerHandler (NULL, BiosKeyboardPrivate); | |
| // | |
| // If there's no key, just return | |
| // | |
| Status = CheckQueue (&BiosKeyboardPrivate->Queue); | |
| if (EFI_ERROR (Status)) { | |
| gBS->RestoreTPL (OldTpl); | |
| return EFI_NOT_READY; | |
| } | |
| Status = Dequeue (&BiosKeyboardPrivate->Queue, KeyData); | |
| gBS->RestoreTPL (OldTpl); | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // EFI Simple Text In Protocol Functions | |
| // | |
| /** | |
| Reset the Keyboard and do BAT test for it, if (ExtendedVerification == TRUE) then do some extra keyboard validations. | |
| @param This Pointer of simple text Protocol. | |
| @param ExtendedVerification Whether perform the extra validation of keyboard. True: perform; FALSE: skip. | |
| @retval EFI_SUCCESS The command byte is written successfully. | |
| @retval EFI_DEVICE_ERROR Errors occurred during resetting keyboard. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardReset ( | |
| IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, | |
| IN BOOLEAN ExtendedVerification | |
| ) | |
| { | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_STATUS Status; | |
| EFI_TPL OldTpl; | |
| UINT8 CommandByte; | |
| BOOLEAN MouseEnable; | |
| EFI_INPUT_KEY Key; | |
| MouseEnable = FALSE; | |
| BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| // | |
| // 1 | |
| // Report reset progress code | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_PROGRESS_CODE, | |
| EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_RESET | |
| ); | |
| // | |
| // Report a Progress Code for clearing the keyboard buffer | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_PROGRESS_CODE, | |
| EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_CLEAR_BUFFER | |
| ); | |
| // | |
| // 2 | |
| // Raise TPL to avoid mouse operation impact | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| // | |
| // | |
| // Exhaust output buffer data | |
| // | |
| do { | |
| Status = BiosKeyboardReadKeyStroke ( | |
| This, | |
| &Key | |
| ); | |
| } while (!EFI_ERROR (Status)); | |
| // | |
| // 3 | |
| // check for KBC itself firstly for setted-up already or not by reading SYSF (bit2) of status register via 64H | |
| // if not skip step 4&5 and jump to step 6 to selftest KBC and report this | |
| // else go step 4 | |
| // | |
| if (!PcdGetBool (PcdFastPS2Detection)) { | |
| if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_SYSF) != 0) { | |
| // | |
| // 4 | |
| // CheckMouseStatus to decide enable it later or not | |
| // | |
| // | |
| // Read the command byte of KBC | |
| // | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_CMDBYTE_R | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardRead ( | |
| BiosKeyboardPrivate, | |
| &CommandByte | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| // | |
| // Check mouse enabled or not before | |
| // | |
| if ((CommandByte & KB_CMMBYTE_DISABLE_AUX) != 0) { | |
| MouseEnable = FALSE; | |
| } else { | |
| MouseEnable = TRUE; | |
| } | |
| // | |
| // 5 | |
| // disable mouse (via KBC) and Keyborad device | |
| // | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_AUX_DISABLE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_KB_DISABLE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| } else { | |
| // | |
| // 6 | |
| // KBC Self Test | |
| // | |
| // | |
| // Report a Progress Code for performing a self test on the keyboard controller | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_PROGRESS_CODE, | |
| EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_SELF_TEST | |
| ); | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_KBC_SLFTEST | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_KBCSLFTEST_OK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| } | |
| } | |
| // | |
| // 7 | |
| // Disable Mouse interface, enable Keyboard interface and declare selftest success | |
| // | |
| // Mouse device will block keyboard interface before it be configured, so we should disable mouse first. | |
| // | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_CMDBYTE_W | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| // | |
| // 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 | |
| // | |
| CommandByte = 0; | |
| Status = KeyboardWrite ( | |
| BiosKeyboardPrivate, | |
| (UINT8) ((CommandByte & | |
| (~KB_CMMBYTE_DISABLE_KB)) | | |
| KB_CMMBYTE_KSCAN2UNI_COV | | |
| KB_CMMBYTE_ENABLE_AUXINT | | |
| KB_CMMBYTE_ENABLE_KBINT | | |
| KB_CMMBYTE_SLFTEST_SUCC | | |
| KB_CMMBYTE_DISABLE_AUX) | |
| ); | |
| // | |
| // For resetting keyboard is not mandatory before booting OS and sometimes keyboard responses very slow, | |
| // so we only do the real resetting for keyboard when user asks, and normally during booting an OS, it's skipped. | |
| // Call CheckKeyboardConnect() to check whether keyboard is connected, if it is not connected, | |
| // Real reset will not do. | |
| // | |
| if (ExtendedVerification && CheckKeyboardConnect (BiosKeyboardPrivate)) { | |
| // | |
| // 8 | |
| // Send keyboard reset command then read ACK | |
| // | |
| Status = KeyboardWrite ( | |
| BiosKeyboardPrivate, | |
| KBC_INPBUF_VIA60_KBRESET | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_ACK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| // | |
| // 9 | |
| // Wait for keyboard return test OK. | |
| // | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_BATTEST_OK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| // | |
| // 10 | |
| // set keyboard scan code set = 02 (standard configuration) | |
| // | |
| Status = KeyboardWrite ( | |
| BiosKeyboardPrivate, | |
| KBC_INPBUF_VIA60_KBSCODE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_ACK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWrite ( | |
| BiosKeyboardPrivate, | |
| KBC_INPBUF_VIA60_SCODESET2 | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_ACK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| // | |
| // 11 | |
| // enable keyboard itself (not via KBC) by writing CMD F4 via 60H | |
| // | |
| Status = KeyboardWrite ( | |
| BiosKeyboardPrivate, | |
| KBC_INPBUF_VIA60_KBEN | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_ACK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| // | |
| // 12 | |
| // Additional validation, do it as follow: | |
| // 1). check for status register of PARE && TIM via 64H | |
| // 2). perform KB checking by writing ABh via 64H | |
| // | |
| if ((KeyReadStatusRegister (BiosKeyboardPrivate) & (KBC_STSREG_VIA64_PARE | KBC_STSREG_VIA64_TIM)) != 0) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_KB_CKECK | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_KBCHECK_OK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| } | |
| // | |
| // 13 | |
| // Done for validating keyboard. Enable keyboard (via KBC) | |
| // and recover the command byte to proper value | |
| // | |
| if (!PcdGetBool (PcdFastPS2Detection)) { | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_KB_ENABLE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| } | |
| // | |
| // 14 | |
| // conditionally enable mouse (via KBC) | |
| // | |
| if (MouseEnable) { | |
| Status = KeyboardCommand ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDREG_VIA64_AUX_ENABLE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| } | |
| } | |
| Exit: | |
| // | |
| // 15 | |
| // resume priority of task level | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Read out the scan code of the key that has just been stroked. | |
| @param This Pointer of simple text Protocol. | |
| @param Key Pointer for store the key that read out. | |
| @retval EFI_SUCCESS The key is read out successfully. | |
| @retval other The key reading failed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardReadKeyStroke ( | |
| IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, | |
| OUT EFI_INPUT_KEY *Key | |
| ) | |
| { | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_STATUS Status; | |
| EFI_KEY_DATA KeyData; | |
| BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| Status = KeyboardReadKeyStrokeWorker (BiosKeyboardPrivate, &KeyData); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Convert the Ctrl+[a-z] to Ctrl+[1-26] | |
| // | |
| if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) { | |
| if (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') { | |
| KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'a' + 1); | |
| } else if (KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') { | |
| KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'A' + 1); | |
| } | |
| } | |
| CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Waiting on the keyboard event, if there's any key pressed by the user, signal the event | |
| @param Event The event that be siganlled when any key has been stroked. | |
| @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_PROTOCOL. | |
| **/ | |
| VOID | |
| EFIAPI | |
| BiosKeyboardWaitForKey ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| // | |
| // Stall 1ms to give a chance to let other driver interrupt this routine for their timer event. | |
| // Csm will be used to check whether there is a key pending, but the csm will disable all | |
| // interrupt before switch to compatibility16, which mean all the efiCompatibility timer | |
| // event will stop work during the compatibility16. And If a caller recursivly invoke this function, | |
| // e.g. UI setup or Shell, other drivers which are driven by timer event will have a bad performance during this period, | |
| // e.g. usb keyboard driver. | |
| // Add a stall period can greatly increate other driver performance during the WaitForKey is recursivly invoked. | |
| // 1ms delay will make little impact to the thunk keyboard driver, and user can not feel the delay at all when input. | |
| // | |
| gBS->Stall (1000); | |
| // | |
| // Use TimerEvent callback function to check whether there's any key pressed | |
| // | |
| BiosKeyboardTimerHandler (NULL, BIOS_KEYBOARD_DEV_FROM_THIS (Context)); | |
| if (!EFI_ERROR (BiosKeyboardCheckForKey (Context))) { | |
| gBS->SignalEvent (Event); | |
| } | |
| } | |
| /** | |
| Check key buffer to get the key stroke status. | |
| @param This Pointer of the protocol EFI_SIMPLE_TEXT_IN_PROTOCOL. | |
| @retval EFI_SUCCESS A key is being pressed now. | |
| @retval Other No key is now pressed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardCheckForKey ( | |
| IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This | |
| ) | |
| { | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| return CheckQueue (&BiosKeyboardPrivate->Queue); | |
| } | |
| // | |
| // Private worker functions | |
| // | |
| #define TABLE_END 0x0 | |
| typedef struct _CONVERT_TABLE_ENTRY { | |
| UINT16 ScanCode; | |
| UINT16 EfiScanCode; | |
| } CONVERT_TABLE_ENTRY; | |
| CONVERT_TABLE_ENTRY mConvertTable[] = { | |
| { | |
| 0x47, | |
| SCAN_HOME | |
| }, | |
| { | |
| 0x48, | |
| SCAN_UP | |
| }, | |
| { | |
| 0x49, | |
| SCAN_PAGE_UP | |
| }, | |
| { | |
| 0x4b, | |
| SCAN_LEFT | |
| }, | |
| { | |
| 0x4d, | |
| SCAN_RIGHT | |
| }, | |
| { | |
| 0x4f, | |
| SCAN_END | |
| }, | |
| { | |
| 0x50, | |
| SCAN_DOWN | |
| }, | |
| { | |
| 0x51, | |
| SCAN_PAGE_DOWN | |
| }, | |
| { | |
| 0x52, | |
| SCAN_INSERT | |
| }, | |
| { | |
| 0x53, | |
| SCAN_DELETE | |
| }, | |
| // | |
| // Function Keys are only valid if KeyChar == 0x00 | |
| // This function does not require KeyChar to be 0x00 | |
| // | |
| { | |
| 0x3b, | |
| SCAN_F1 | |
| }, | |
| { | |
| 0x3c, | |
| SCAN_F2 | |
| }, | |
| { | |
| 0x3d, | |
| SCAN_F3 | |
| }, | |
| { | |
| 0x3e, | |
| SCAN_F4 | |
| }, | |
| { | |
| 0x3f, | |
| SCAN_F5 | |
| }, | |
| { | |
| 0x40, | |
| SCAN_F6 | |
| }, | |
| { | |
| 0x41, | |
| SCAN_F7 | |
| }, | |
| { | |
| 0x42, | |
| SCAN_F8 | |
| }, | |
| { | |
| 0x43, | |
| SCAN_F9 | |
| }, | |
| { | |
| 0x44, | |
| SCAN_F10 | |
| }, | |
| { | |
| 0x85, | |
| SCAN_F11 | |
| }, | |
| { | |
| 0x86, | |
| SCAN_F12 | |
| }, | |
| // | |
| // Convert ALT + Fn keys | |
| // | |
| { | |
| 0x68, | |
| SCAN_F1 | |
| }, | |
| { | |
| 0x69, | |
| SCAN_F2 | |
| }, | |
| { | |
| 0x6a, | |
| SCAN_F3 | |
| }, | |
| { | |
| 0x6b, | |
| SCAN_F4 | |
| }, | |
| { | |
| 0x6c, | |
| SCAN_F5 | |
| }, | |
| { | |
| 0x6d, | |
| SCAN_F6 | |
| }, | |
| { | |
| 0x6e, | |
| SCAN_F7 | |
| }, | |
| { | |
| 0x6f, | |
| SCAN_F8 | |
| }, | |
| { | |
| 0x70, | |
| SCAN_F9 | |
| }, | |
| { | |
| 0x71, | |
| SCAN_F10 | |
| }, | |
| { | |
| TABLE_END, | |
| SCAN_NULL | |
| }, | |
| }; | |
| /** | |
| Convert unicode combined with scan code of key to the counterpart of EFIScancode of it. | |
| @param KeyChar Unicode of key. | |
| @param ScanCode Scan code of key. | |
| @return The value of EFI Scancode for the key. | |
| @retval SCAN_NULL No corresponding value in the EFI convert table is found for the key. | |
| **/ | |
| UINT16 | |
| ConvertToEFIScanCode ( | |
| IN CHAR16 KeyChar, | |
| IN UINT16 ScanCode | |
| ) | |
| { | |
| UINT16 EfiScanCode; | |
| UINT16 Index; | |
| if (KeyChar == CHAR_ESC) { | |
| EfiScanCode = SCAN_ESC; | |
| } else if (KeyChar == 0x00 || KeyChar == 0xe0) { | |
| // | |
| // Movement & Function Keys | |
| // | |
| for (Index = 0; (Index < sizeof (mConvertTable) / sizeof (CONVERT_TABLE_ENTRY)) && (mConvertTable[Index].ScanCode != TABLE_END); Index += 1) { | |
| if (ScanCode == mConvertTable[Index].ScanCode) { | |
| return mConvertTable[Index].EfiScanCode; | |
| } | |
| } | |
| // | |
| // Reach Table end, return default value | |
| // | |
| return SCAN_NULL; | |
| } else { | |
| return SCAN_NULL; | |
| } | |
| return EfiScanCode; | |
| } | |
| /** | |
| 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 BiosKeyboardPrivate Keyboard Private Data Struture | |
| @retval TRUE Keyboard in System. | |
| @retval FALSE Keyboard not in System. | |
| **/ | |
| BOOLEAN | |
| CheckKeyboardConnect ( | |
| IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = EFI_SUCCESS; | |
| // | |
| // enable keyboard itself and wait for its ack | |
| // If can't receive ack, Keyboard should not be connected. | |
| // | |
| if (!PcdGetBool (PcdFastPS2Detection)) { | |
| Status = KeyboardWrite ( | |
| BiosKeyboardPrivate, | |
| KBC_INPBUF_VIA60_KBEN | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_ERROR, "[KBD]CheckKeyboardConnect - Keyboard enable failed!\n")); | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
| EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR | |
| ); | |
| return FALSE; | |
| } | |
| Status = KeyboardWaitForValue ( | |
| BiosKeyboardPrivate, | |
| KBC_CMDECHO_ACK, | |
| KEYBOARD_WAITFORVALUE_TIMEOUT | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_ERROR, "[KBD]CheckKeyboardConnect - Timeout!\n")); | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
| EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR | |
| ); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } else { | |
| return TRUE; | |
| } | |
| } | |
| /** | |
| Timer event handler: read a series of key stroke from 8042 | |
| and put them into memory key buffer. | |
| It is registered as running under TPL_NOTIFY | |
| @param Event The timer event | |
| @param Context A BIOS_KEYBOARD_DEV pointer | |
| **/ | |
| VOID | |
| EFIAPI | |
| BiosKeyboardTimerHandler ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_TPL OldTpl; | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_IA32_REGISTER_SET Regs; | |
| UINT8 KbFlag1; // 0040h:0017h - KEYBOARD - STATUS FLAGS 1 | |
| UINT8 KbFlag2; // 0040h:0018h - KEYBOARD - STATUS FLAGS 2 | |
| EFI_KEY_DATA KeyData; | |
| LIST_ENTRY *Link; | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| BiosKeyboardPrivate = Context; | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| // | |
| // if there is no key present, just return | |
| // | |
| if (BiosKeyboardPrivate->ExtendedKeyboard) { | |
| Regs.H.AH = 0x11; | |
| } else { | |
| Regs.H.AH = 0x01; | |
| } | |
| BiosKeyboardPrivate->LegacyBios->Int86 ( | |
| BiosKeyboardPrivate->LegacyBios, | |
| 0x16, | |
| &Regs | |
| ); | |
| if (Regs.X.Flags.ZF != 0) { | |
| gBS->RestoreTPL (OldTpl); | |
| return; | |
| } | |
| // | |
| // Read the key | |
| // | |
| if (BiosKeyboardPrivate->ExtendedKeyboard) { | |
| Regs.H.AH = 0x10; | |
| } else { | |
| Regs.H.AH = 0x00; | |
| } | |
| BiosKeyboardPrivate->LegacyBios->Int86 ( | |
| BiosKeyboardPrivate->LegacyBios, | |
| 0x16, | |
| &Regs | |
| ); | |
| KeyData.Key.ScanCode = (UINT16) Regs.H.AH; | |
| KeyData.Key.UnicodeChar = (UINT16) Regs.H.AL; | |
| DEBUG (( | |
| EFI_D_INFO, | |
| "[KBD]INT16 returns EFI_INPUT_KEY.ScanCode - %x, EFI_INPUT_KEY.UnicodeChar - %x\n", | |
| KeyData.Key.ScanCode, | |
| KeyData.Key.UnicodeChar | |
| )); | |
| KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID; | |
| KeyData.KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID; | |
| // | |
| // Leagcy Bios use Int 9 which is IRQ1 interrupt handler to get keystroke scancode to KB buffer in BDA (BIOS DATE AREA), then | |
| // Int 16 depend KB buffer and some key bits in BDA to translate the scancode to ASCII code, and return both the scancode and ASCII | |
| // code to Int 16 caller. This translation process works well if the Int 9 could response user input in time. But in Tiano enviorment, the Int 9 | |
| // will be disabled after the thunk call finish, which means if user crazy input during int 9 being disabled, some keystrokes will be lost when | |
| // KB device own hardware buffer overflows. And if the lost keystroke code is CTRL or ALT or SHIFT release code, these function key flags bit | |
| // in BDA will not be updated. So the Int 16 will believe the CTRL or ALT or SHIFT is still pressed, and Int 16 will translate later scancode | |
| // to wrong ASCII code. We can increase the Thunk frequence to let Int 9 response in time, but this way will much hurt other drivers | |
| // performance, like USB. | |
| // | |
| // 1. If CTRL or ALT release code is missed, all later input keys will be translated to wrong ASCII codes which the Tiano cannot support. In | |
| // this case, the KB input seems fail to work, and user input is blocked. To solve the problem, we can help to clear the CTRL or ALT flag in BDA | |
| // after every Int 16 finish. Thus persist to press CTRL or ALT has same effection as only press one time. It is Ok, since user not often use the | |
| // CTRL and ALT. | |
| // | |
| // 2. If SHIFT release code is missed, all later lowercase input will become capital. This is ugly, but not block user input. If user press the lost | |
| // SHIFT again, the lowercase will come back to normal. Since user often use the SHIFT, it is not reasonable to help to clear the SHIFT flag in BDA, | |
| // which will let persist to press SHIFT has same effection as only press one time. | |
| // | |
| //0040h:0017h - KEYBOARD - STATUS FLAGS 1 | |
| // 7 INSert active | |
| // 6 Caps Lock active | |
| // 5 Num Lock active | |
| // 4 Scroll Lock active | |
| // 3 either Alt pressed | |
| // 2 either Ctrl pressed | |
| // 1 Left Shift pressed | |
| // 0 Right Shift pressed | |
| // | |
| // Clear the CTRL and ALT BDA flag | |
| // | |
| KbFlag1 = *((UINT8 *) (UINTN) 0x417); // read the STATUS FLAGS 1 | |
| KbFlag2 = *((UINT8 *) (UINTN) 0x418); // read STATUS FLAGS 2 | |
| DEBUG_CODE ( | |
| { | |
| if ((KbFlag1 & KB_CAPS_LOCK_BIT) == KB_CAPS_LOCK_BIT) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Caps Lock Key is pressed.\n")); | |
| } | |
| if ((KbFlag1 & KB_NUM_LOCK_BIT) == KB_NUM_LOCK_BIT) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Num Lock Key is pressed.\n")); | |
| } | |
| if ((KbFlag1 & KB_SCROLL_LOCK_BIT) == KB_SCROLL_LOCK_BIT) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Scroll Lock Key is pressed.\n")); | |
| } | |
| if ((KbFlag1 & KB_ALT_PRESSED) == KB_ALT_PRESSED) { | |
| if ((KbFlag2 & KB_LEFT_ALT_PRESSED) == KB_LEFT_ALT_PRESSED) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Left Alt Key is pressed.\n")); | |
| } else { | |
| DEBUG ((EFI_D_INFO, "[KBD]Right Alt Key is pressed.\n")); | |
| } | |
| } | |
| if ((KbFlag1 & KB_CTRL_PRESSED) == KB_CTRL_PRESSED) { | |
| if ((KbFlag2 & KB_LEFT_CTRL_PRESSED) == KB_LEFT_CTRL_PRESSED) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Left Ctrl Key is pressed.\n")); | |
| } else { | |
| DEBUG ((EFI_D_INFO, "[KBD]Right Ctrl Key is pressed.\n")); | |
| } | |
| } | |
| if ((KbFlag1 & KB_LEFT_SHIFT_PRESSED) == KB_LEFT_SHIFT_PRESSED) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Left Shift Key is pressed.\n")); | |
| } | |
| if ((KbFlag1 & KB_RIGHT_SHIFT_PRESSED) == KB_RIGHT_SHIFT_PRESSED) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Right Shift Key is pressed.\n")); | |
| } | |
| } | |
| ); | |
| // | |
| // Record toggle state | |
| // | |
| if ((KbFlag1 & KB_CAPS_LOCK_BIT) == KB_CAPS_LOCK_BIT) { | |
| KeyData.KeyState.KeyToggleState |= EFI_CAPS_LOCK_ACTIVE; | |
| } | |
| if ((KbFlag1 & KB_NUM_LOCK_BIT) == KB_NUM_LOCK_BIT) { | |
| KeyData.KeyState.KeyToggleState |= EFI_NUM_LOCK_ACTIVE; | |
| } | |
| if ((KbFlag1 & KB_SCROLL_LOCK_BIT) == KB_SCROLL_LOCK_BIT) { | |
| KeyData.KeyState.KeyToggleState |= EFI_SCROLL_LOCK_ACTIVE; | |
| } | |
| // | |
| // Record shift state | |
| // BUGBUG: Need add Menu key and Left/Right Logo key state in the future | |
| // | |
| if ((KbFlag1 & KB_ALT_PRESSED) == KB_ALT_PRESSED) { | |
| KeyData.KeyState.KeyShiftState |= ((KbFlag2 & KB_LEFT_ALT_PRESSED) == KB_LEFT_ALT_PRESSED) ? EFI_LEFT_ALT_PRESSED : EFI_RIGHT_ALT_PRESSED; | |
| } | |
| if ((KbFlag1 & KB_CTRL_PRESSED) == KB_CTRL_PRESSED) { | |
| KeyData.KeyState.KeyShiftState |= ((KbFlag2 & KB_LEFT_CTRL_PRESSED) == KB_LEFT_CTRL_PRESSED) ? EFI_LEFT_CONTROL_PRESSED : EFI_RIGHT_CONTROL_PRESSED; | |
| } | |
| if ((KbFlag1 & KB_LEFT_SHIFT_PRESSED) == KB_LEFT_SHIFT_PRESSED) { | |
| KeyData.KeyState.KeyShiftState |= EFI_LEFT_SHIFT_PRESSED; | |
| } | |
| if ((KbFlag1 & KB_RIGHT_SHIFT_PRESSED) == KB_RIGHT_SHIFT_PRESSED) { | |
| KeyData.KeyState.KeyShiftState |= EFI_RIGHT_SHIFT_PRESSED; | |
| } | |
| // | |
| // Clear left alt and left ctrl BDA flag | |
| // | |
| KbFlag2 &= ~(KB_LEFT_ALT_PRESSED | KB_LEFT_CTRL_PRESSED); | |
| *((UINT8 *) (UINTN) 0x418) = KbFlag2; | |
| KbFlag1 &= ~0x0C; | |
| *((UINT8 *) (UINTN) 0x417) = KbFlag1; | |
| // | |
| // Output EFI input key and shift/toggle state | |
| // | |
| if (KeyData.Key.UnicodeChar == CHAR_NULL || KeyData.Key.UnicodeChar == CHAR_SCANCODE || KeyData.Key.UnicodeChar == CHAR_ESC) { | |
| KeyData.Key.ScanCode = ConvertToEFIScanCode (KeyData.Key.UnicodeChar, KeyData.Key.ScanCode); | |
| KeyData.Key.UnicodeChar = CHAR_NULL; | |
| } else { | |
| KeyData.Key.ScanCode = SCAN_NULL; | |
| } | |
| // | |
| // CSM16 has converted the Ctrl+[a-z] to [1-26], converted it back. | |
| // | |
| if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) { | |
| if (KeyData.Key.UnicodeChar >= 1 && KeyData.Key.UnicodeChar <= 26) { | |
| if (((KeyData.KeyState.KeyShiftState & (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)) != 0) == | |
| ((KeyData.KeyState.KeyToggleState & EFI_CAPS_LOCK_ACTIVE) != 0) | |
| ) { | |
| KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar + L'a' - 1); | |
| } else { | |
| KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar + L'A' - 1); | |
| } | |
| } | |
| } | |
| DEBUG (( | |
| EFI_D_INFO, | |
| "[KBD]Convert to EFI Scan Code, EFI_INPUT_KEY.ScanCode - %x, EFI_INPUT_KEY.UnicodeChar - %x\n", | |
| KeyData.Key.ScanCode, | |
| KeyData.Key.UnicodeChar | |
| )); | |
| // | |
| // 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'; Shift Key + 'F' key = 'f'. | |
| // | |
| if ((KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') || | |
| (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') | |
| ) { | |
| DEBUG ((EFI_D_INFO, "[KBD]Shift key with a~z are pressed, remove shift state in EFI_KEY_STATE.\n")); | |
| KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED); | |
| } | |
| // | |
| // Signal KeyNotify process event if this key pressed matches any key registered. | |
| // | |
| for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { | |
| CurrentNotify = CR ( | |
| Link, | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| BIOS_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. | |
| // | |
| Enqueue (&BiosKeyboardPrivate->QueueForNotify, &KeyData); | |
| gBS->SignalEvent (BiosKeyboardPrivate->KeyNotifyProcessEvent); | |
| } | |
| } | |
| Enqueue (&BiosKeyboardPrivate->Queue, &KeyData); | |
| // | |
| // Leave critical section and return | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| return ; | |
| } | |
| /** | |
| Process key notify. | |
| @param Event Indicates the event that invoke this function. | |
| @param Context Indicates the calling context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| KeyNotifyProcessHandler ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_KEY_DATA KeyData; | |
| LIST_ENTRY *Link; | |
| LIST_ENTRY *NotifyList; | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| EFI_TPL OldTpl; | |
| BiosKeyboardPrivate = (BIOS_KEYBOARD_DEV *) Context; | |
| // | |
| // Invoke notification functions. | |
| // | |
| NotifyList = &BiosKeyboardPrivate->NotifyList; | |
| while (TRUE) { | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| Status = Dequeue (&BiosKeyboardPrivate->QueueForNotify, &KeyData); | |
| // | |
| // Leave critical section | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| if (EFI_ERROR (Status)) { | |
| break; | |
| } | |
| for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) { | |
| CurrentNotify = CR (Link, BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, NotifyEntry, BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE); | |
| if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { | |
| CurrentNotify->KeyNotificationFn (&KeyData); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Free keyboard notify list. | |
| @param ListHead The list head | |
| @retval EFI_SUCCESS Free the notify list successfully | |
| @retval EFI_INVALID_PARAMETER ListHead is invalid. | |
| **/ | |
| EFI_STATUS | |
| BiosKeyboardFreeNotifyList ( | |
| IN OUT LIST_ENTRY *ListHead | |
| ) | |
| { | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; | |
| if (ListHead == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| while (!IsListEmpty (ListHead)) { | |
| NotifyNode = CR ( | |
| ListHead->ForwardLink, | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE | |
| ); | |
| RemoveEntryList (ListHead->ForwardLink); | |
| gBS->FreePool (NotifyNode); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check if key is registered. | |
| @param RegsiteredData A pointer to a buffer that is filled in with the keystroke | |
| state data for the key that was registered. | |
| @param InputData A pointer to a buffer that is filled in with the keystroke | |
| state data for the key that was pressed. | |
| @retval TRUE Key be pressed matches a registered key. | |
| @retval FLASE Match failed. | |
| **/ | |
| BOOLEAN | |
| IsKeyRegistered ( | |
| IN EFI_KEY_DATA *RegsiteredData, | |
| IN EFI_KEY_DATA *InputData | |
| ) | |
| { | |
| ASSERT (RegsiteredData != NULL && InputData != NULL); | |
| if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) || | |
| (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) { | |
| return FALSE; | |
| } | |
| // | |
| // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored. | |
| // | |
| if (RegsiteredData->KeyState.KeyShiftState != 0 && | |
| RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState) { | |
| return FALSE; | |
| } | |
| if (RegsiteredData->KeyState.KeyToggleState != 0 && | |
| RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState) { | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Waiting on the keyboard event, if there's any key pressed by the user, signal the event | |
| @param Event The event that be siganlled when any key has been stroked. | |
| @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. | |
| **/ | |
| VOID | |
| EFIAPI | |
| BiosKeyboardWaitForKeyEx ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (Context); | |
| BiosKeyboardWaitForKey (Event, &BiosKeyboardPrivate->SimpleTextIn); | |
| } | |
| /** | |
| Reset the input device and optionaly run diagnostics | |
| @param This Protocol instance pointer. | |
| @param ExtendedVerification Driver may perform diagnostics on reset. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_DEVICE_ERROR The device is not functioning properly and could | |
| not be reset. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardResetEx ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| IN BOOLEAN ExtendedVerification | |
| ) | |
| { | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_STATUS Status; | |
| EFI_TPL OldTpl; | |
| BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| Status = BiosKeyboardPrivate->SimpleTextIn.Reset ( | |
| &BiosKeyboardPrivate->SimpleTextIn, | |
| ExtendedVerification | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| gBS->RestoreTPL (OldTpl); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Reads the next keystroke from the input device. The WaitForKey Event can | |
| be used to test for existance of a keystroke via WaitForEvent () call. | |
| @param This Protocol instance pointer. | |
| @param KeyData A pointer to a buffer that is filled in with the keystroke | |
| state data for the key that was pressed. | |
| @retval EFI_SUCCESS The keystroke information was returned. | |
| @retval EFI_NOT_READY There was no keystroke data availiable. | |
| @retval EFI_DEVICE_ERROR The keystroke information was not returned due to | |
| hardware errors. | |
| @retval EFI_INVALID_PARAMETER KeyData is NULL. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardReadKeyStrokeEx ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| OUT EFI_KEY_DATA *KeyData | |
| ) | |
| { | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| if (KeyData == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| return KeyboardReadKeyStrokeWorker (BiosKeyboardPrivate, KeyData); | |
| } | |
| /** | |
| Set certain state for the input device. | |
| @param This Protocol instance pointer. | |
| @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the | |
| state for the input device. | |
| @retval EFI_SUCCESS The device state was set successfully. | |
| @retval EFI_DEVICE_ERROR The device is not functioning correctly and could | |
| not have the setting adjusted. | |
| @retval EFI_UNSUPPORTED The device does not have the ability to set its state. | |
| @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardSetState ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| IN EFI_KEY_TOGGLE_STATE *KeyToggleState | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_TPL OldTpl; | |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; | |
| UINT8 Command; | |
| if (KeyToggleState == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Thunk keyboard driver doesn't support partial keystroke. | |
| // | |
| if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID || | |
| (*KeyToggleState & EFI_KEY_STATE_EXPOSED) == EFI_KEY_STATE_EXPOSED | |
| ) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| // | |
| // See if the Legacy BIOS Protocol is available | |
| // | |
| Status = gBS->LocateProtocol ( | |
| &gEfiLegacyBiosProtocolGuid, | |
| NULL, | |
| (VOID **) &LegacyBios | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| Command = 0; | |
| if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) { | |
| Command |= 4; | |
| } | |
| if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) { | |
| Command |= 2; | |
| } | |
| if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) { | |
| Command |= 1; | |
| } | |
| Status = KeyboardWrite (BiosKeyboardPrivate, 0xed); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWaitForValue (BiosKeyboardPrivate, 0xfa, KEYBOARD_WAITFORVALUE_TIMEOUT); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| Status = KeyboardWrite (BiosKeyboardPrivate, Command); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_DEVICE_ERROR; | |
| goto Exit; | |
| } | |
| // | |
| // Call Legacy BIOS Protocol to set whatever is necessary | |
| // | |
| LegacyBios->UpdateKeyboardLedStatus (LegacyBios, Command); | |
| Status = EFI_SUCCESS; | |
| Exit: | |
| // | |
| // Leave critical section and return | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Register a notification function for a particular keystroke for the input device. | |
| @param This Protocol instance pointer. | |
| @param KeyData A pointer to a buffer that is filled in with the keystroke | |
| information data for the key that was pressed. | |
| @param KeyNotificationFunction Points to the function to be called when the key | |
| sequence is typed specified by KeyData. | |
| @param NotifyHandle Points to the unique handle assigned to the registered notification. | |
| @retval EFI_SUCCESS The notification function was registered successfully. | |
| @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data structures. | |
| @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardRegisterKeyNotify ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| IN EFI_KEY_DATA *KeyData, | |
| IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, | |
| OUT VOID **NotifyHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_TPL OldTpl; | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify; | |
| LIST_ENTRY *Link; | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| // | |
| // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. | |
| // | |
| for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { | |
| CurrentNotify = CR ( | |
| Link, | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE | |
| ); | |
| if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { | |
| if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { | |
| *NotifyHandle = CurrentNotify; | |
| Status = EFI_SUCCESS; | |
| goto Exit; | |
| } | |
| } | |
| } | |
| // | |
| // Allocate resource to save the notification function | |
| // | |
| NewNotify = (BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY)); | |
| if (NewNotify == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Exit; | |
| } | |
| NewNotify->Signature = BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE; | |
| NewNotify->KeyNotificationFn = KeyNotificationFunction; | |
| CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); | |
| InsertTailList (&BiosKeyboardPrivate->NotifyList, &NewNotify->NotifyEntry); | |
| *NotifyHandle = NewNotify; | |
| Status = EFI_SUCCESS; | |
| Exit: | |
| // | |
| // Leave critical section and return | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Remove a registered notification function from a particular keystroke. | |
| @param This Protocol instance pointer. | |
| @param NotificationHandle The handle of the notification function being unregistered. | |
| @retval EFI_SUCCESS The notification function was unregistered successfully. | |
| @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BiosKeyboardUnregisterKeyNotify ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| IN VOID *NotificationHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; | |
| EFI_TPL OldTpl; | |
| LIST_ENTRY *Link; | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| // | |
| // Check incoming notification handle | |
| // | |
| if (NotificationHandle == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (((BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) NotificationHandle)->Signature != BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { | |
| CurrentNotify = CR ( | |
| Link, | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE | |
| ); | |
| if (CurrentNotify == NotificationHandle) { | |
| // | |
| // Remove the notification function from NotifyList and free resources | |
| // | |
| RemoveEntryList (&CurrentNotify->NotifyEntry); | |
| Status = EFI_SUCCESS; | |
| goto Exit; | |
| } | |
| } | |
| // | |
| // Can not find the specified Notification Handle | |
| // | |
| Status = EFI_INVALID_PARAMETER; | |
| Exit: | |
| // | |
| // Leave critical section and return | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| The user Entry Point for module BiosKeyboard. The user code starts with this function. | |
| @param[in] ImageHandle The firmware allocated handle for the EFI image. | |
| @param[in] SystemTable A pointer to the EFI System Table. | |
| @retval EFI_SUCCESS The entry point is executed successfully. | |
| @retval other Some error occurs when executing this entry point. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| InitializeBiosKeyboard( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Install driver model protocol(s). | |
| // | |
| Status = EfiLibInstallDriverBindingComponentName2 ( | |
| ImageHandle, | |
| SystemTable, | |
| &gBiosKeyboardDriverBinding, | |
| ImageHandle, | |
| &gBiosKeyboardComponentName, | |
| &gBiosKeyboardComponentName2 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } |