| /** @file | |
| VirtualKeyboard driver | |
| Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
| Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "VirtualKeyboard.h" | |
| // | |
| // RAM Keyboard Driver Binding Protocol Instance | |
| // | |
| EFI_DRIVER_BINDING_PROTOCOL gVirtualKeyboardDriverBinding = { | |
| VirtualKeyboardDriverBindingSupported, | |
| VirtualKeyboardDriverBindingStart, | |
| VirtualKeyboardDriverBindingStop, | |
| 0x10, | |
| NULL, | |
| NULL | |
| }; | |
| // | |
| // 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 | |
| VirtualKeyboardDriverBindingSupported ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual; | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gPlatformVirtualKeyboardProtocolGuid, | |
| (VOID **)&PlatformVirtual, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gPlatformVirtualKeyboardProtocolGuid, | |
| 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 | |
| VirtualKeyboardDriverBindingStart ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual; | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gPlatformVirtualKeyboardProtocolGuid, | |
| (VOID **)&PlatformVirtual, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Allocate the private device structure | |
| // | |
| VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *)AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_DEV)); | |
| if (VirtualKeyboardPrivate == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Done; | |
| } | |
| // | |
| // Initialize the private device structure | |
| // | |
| VirtualKeyboardPrivate->Signature = VIRTUAL_KEYBOARD_DEV_SIGNATURE; | |
| VirtualKeyboardPrivate->Handle = Controller; | |
| VirtualKeyboardPrivate->PlatformVirtual = PlatformVirtual; | |
| VirtualKeyboardPrivate->Queue.Front = 0; | |
| VirtualKeyboardPrivate->Queue.Rear = 0; | |
| VirtualKeyboardPrivate->QueueForNotify.Front = 0; | |
| VirtualKeyboardPrivate->QueueForNotify.Rear = 0; | |
| VirtualKeyboardPrivate->SimpleTextIn.Reset = VirtualKeyboardReset; | |
| VirtualKeyboardPrivate->SimpleTextIn.ReadKeyStroke = VirtualKeyboardReadKeyStroke; | |
| VirtualKeyboardPrivate->SimpleTextInputEx.Reset = VirtualKeyboardResetEx; | |
| VirtualKeyboardPrivate->SimpleTextInputEx.ReadKeyStrokeEx = VirtualKeyboardReadKeyStrokeEx; | |
| VirtualKeyboardPrivate->SimpleTextInputEx.SetState = VirtualKeyboardSetState; | |
| VirtualKeyboardPrivate->SimpleTextInputEx.RegisterKeyNotify = VirtualKeyboardRegisterKeyNotify; | |
| VirtualKeyboardPrivate->SimpleTextInputEx.UnregisterKeyNotify = VirtualKeyboardUnregisterKeyNotify; | |
| InitializeListHead (&VirtualKeyboardPrivate->NotifyList); | |
| Status = PlatformVirtual->Register (); | |
| if (EFI_ERROR (Status)) { | |
| goto Done; | |
| } | |
| // | |
| // 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, | |
| VirtualKeyboardWaitForKey, | |
| &(VirtualKeyboardPrivate->SimpleTextIn), | |
| &((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| (VirtualKeyboardPrivate->SimpleTextIn).WaitForKey = NULL; | |
| goto Done; | |
| } | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_WAIT, | |
| TPL_NOTIFY, | |
| VirtualKeyboardWaitForKeyEx, | |
| &(VirtualKeyboardPrivate->SimpleTextInputEx), | |
| &(VirtualKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| VirtualKeyboardPrivate->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, | |
| VirtualKeyboardTimerHandler, | |
| VirtualKeyboardPrivate, | |
| &VirtualKeyboardPrivate->TimerEvent | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Done; | |
| } | |
| Status = gBS->SetTimer ( | |
| VirtualKeyboardPrivate->TimerEvent, | |
| TimerPeriodic, | |
| KEYBOARD_TIMER_INTERVAL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Done; | |
| } | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| KeyNotifyProcessHandler, | |
| VirtualKeyboardPrivate, | |
| &VirtualKeyboardPrivate->KeyNotifyProcessEvent | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Done; | |
| } | |
| // | |
| // Reset the keyboard device | |
| // | |
| Status = VirtualKeyboardPrivate->SimpleTextInputEx.Reset ( | |
| &VirtualKeyboardPrivate->SimpleTextInputEx, | |
| FALSE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "[KBD]Reset Failed. Status - %r\n", Status)); | |
| goto Done; | |
| } | |
| // | |
| // Install protocol interfaces for the keyboard device. | |
| // | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &Controller, | |
| &gEfiSimpleTextInProtocolGuid, | |
| &VirtualKeyboardPrivate->SimpleTextIn, | |
| &gEfiSimpleTextInputExProtocolGuid, | |
| &VirtualKeyboardPrivate->SimpleTextInputEx, | |
| NULL | |
| ); | |
| Done: | |
| if (EFI_ERROR (Status)) { | |
| if (VirtualKeyboardPrivate != NULL) { | |
| if ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey != NULL) { | |
| gBS->CloseEvent ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey); | |
| } | |
| if ((VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx != NULL) { | |
| gBS->CloseEvent ( | |
| (VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx | |
| ); | |
| } | |
| if (VirtualKeyboardPrivate->KeyNotifyProcessEvent != NULL) { | |
| gBS->CloseEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent); | |
| } | |
| VirtualKeyboardFreeNotifyList (&VirtualKeyboardPrivate->NotifyList); | |
| if (VirtualKeyboardPrivate->TimerEvent != NULL) { | |
| gBS->CloseEvent (VirtualKeyboardPrivate->TimerEvent); | |
| } | |
| FreePool (VirtualKeyboardPrivate); | |
| } | |
| } | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gPlatformVirtualKeyboardProtocolGuid, | |
| 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 | |
| VirtualKeyboardDriverBindingStop ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN UINTN NumberOfChildren, | |
| IN EFI_HANDLE *ChildHandleBuffer | |
| ) | |
| { | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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)); | |
| ZeroMem (&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; | |
| } | |
| /** | |
| 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 | |
| VirtualKeyboardCheckForKey ( | |
| IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This | |
| ) | |
| { | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); | |
| return CheckQueue (&VirtualKeyboardPrivate->Queue); | |
| } | |
| /** | |
| 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 | |
| VirtualKeyboardFreeNotifyList ( | |
| IN OUT LIST_ENTRY *ListHead | |
| ) | |
| { | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; | |
| if (ListHead == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| while (!IsListEmpty (ListHead)) { | |
| NotifyNode = CR ( | |
| ListHead->ForwardLink, | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE | |
| ); | |
| RemoveEntryList (ListHead->ForwardLink); | |
| gBS->FreePool (NotifyNode); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Judge whether is a registed key | |
| @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 FALSE 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; | |
| } | |
| /** | |
| Event notification function for SIMPLE_TEXT_IN.WaitForKey event | |
| Signal the event if there is key available | |
| @param Event the event object | |
| @param Context waiting context | |
| **/ | |
| VOID | |
| EFIAPI | |
| VirtualKeyboardWaitForKey ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| // | |
| // Stall 1ms to give a chance to let other driver interrupt this routine | |
| // for their timer event. | |
| // 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 | |
| // | |
| VirtualKeyboardTimerHandler (NULL, VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context)); | |
| if (!EFI_ERROR (VirtualKeyboardCheckForKey (Context))) { | |
| gBS->SignalEvent (Event); | |
| } | |
| } | |
| /** | |
| Event notification function for SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx | |
| event. Signal the event if there is key available | |
| @param Event event object | |
| @param Context waiting context | |
| **/ | |
| VOID | |
| EFIAPI | |
| VirtualKeyboardWaitForKeyEx ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context); | |
| VirtualKeyboardWaitForKey (Event, &VirtualKeyboardPrivate->SimpleTextIn); | |
| } | |
| // | |
| // 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 | |
| VirtualKeyboardReset ( | |
| IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, | |
| IN BOOLEAN ExtendedVerification | |
| ) | |
| { | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| EFI_STATUS Status; | |
| EFI_TPL OldTpl; | |
| VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); | |
| // | |
| // Raise TPL to avoid mouse operation impact | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| if (VirtualKeyboardPrivate->PlatformVirtual && | |
| VirtualKeyboardPrivate->PlatformVirtual->Reset) | |
| { | |
| Status = VirtualKeyboardPrivate->PlatformVirtual->Reset (); | |
| } else { | |
| Status = EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // resume priority of task level | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Reset the input device and optionally 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 | |
| VirtualKeyboardResetEx ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| IN BOOLEAN ExtendedVerification | |
| ) | |
| { | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| EFI_STATUS Status; | |
| EFI_TPL OldTpl; | |
| VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); | |
| Status = VirtualKeyboardPrivate->SimpleTextIn.Reset ( | |
| &VirtualKeyboardPrivate->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 existence of a keystroke via WaitForEvent () call. | |
| @param VirtualKeyboardPrivate Virtualkeyboard 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 available. | |
| @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 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate, | |
| 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. | |
| // 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); | |
| VirtualKeyboardTimerHandler (NULL, VirtualKeyboardPrivate); | |
| // | |
| // If there's no key, just return | |
| // | |
| Status = CheckQueue (&VirtualKeyboardPrivate->Queue); | |
| if (EFI_ERROR (Status)) { | |
| gBS->RestoreTPL (OldTpl); | |
| return EFI_NOT_READY; | |
| } | |
| Status = Dequeue (&VirtualKeyboardPrivate->Queue, KeyData); | |
| gBS->RestoreTPL (OldTpl); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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. | |
| @retval EFI_UNSUPPORTED The device does not support the ability to read keystroke data. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| VirtualKeyboardReadKeyStroke ( | |
| IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, | |
| OUT EFI_INPUT_KEY *Key | |
| ) | |
| { | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| EFI_STATUS Status; | |
| EFI_KEY_DATA KeyData; | |
| VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); | |
| Status = KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, &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; | |
| } | |
| /** | |
| Reads the next keystroke from the input device. The WaitForKey Event can | |
| be used to test for existence 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 available. | |
| @retval EFI_DEVICE_ERROR The keystroke information was not returned | |
| due to hardware errors. | |
| @retval EFI_INVALID_PARAMETER KeyData is NULL. | |
| @retval EFI_UNSUPPORTED The device does not support the ability to read keystroke data. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| VirtualKeyboardReadKeyStrokeEx ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| OUT EFI_KEY_DATA *KeyData | |
| ) | |
| { | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| if (KeyData == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); | |
| return KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, 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 | |
| VirtualKeyboardSetState ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| IN EFI_KEY_TOGGLE_STATE *KeyToggleState | |
| ) | |
| { | |
| if (KeyToggleState == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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 necessary | |
| data structures. | |
| @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| VirtualKeyboardRegisterKeyNotify ( | |
| 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; | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| EFI_TPL OldTpl; | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify; | |
| LIST_ENTRY *Link; | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| if ((KeyData == NULL) || | |
| (NotifyHandle == NULL) || | |
| (KeyNotificationFunction == NULL)) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_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 = VirtualKeyboardPrivate->NotifyList.ForwardLink; | |
| Link != &VirtualKeyboardPrivate->NotifyList; | |
| Link = Link->ForwardLink) | |
| { | |
| CurrentNotify = CR ( | |
| Link, | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| VIRTUAL_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 = (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *)AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY)); | |
| if (NewNotify == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Exit; | |
| } | |
| NewNotify->Signature = VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE; | |
| NewNotify->KeyNotificationFn = KeyNotificationFunction; | |
| CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); | |
| InsertTailList (&VirtualKeyboardPrivate->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 | |
| VirtualKeyboardUnregisterKeyNotify ( | |
| IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
| IN VOID *NotificationHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| EFI_TPL OldTpl; | |
| LIST_ENTRY *Link; | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| // | |
| // Check incoming notification handle | |
| // | |
| if (NotificationHandle == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (((VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *)NotificationHandle)->Signature != | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; | |
| Link != &VirtualKeyboardPrivate->NotifyList; | |
| Link = Link->ForwardLink) | |
| { | |
| CurrentNotify = CR ( | |
| Link, | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| VIRTUAL_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; | |
| } | |
| /** | |
| 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 | |
| VirtualKeyboardTimerHandler ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_TPL OldTpl; | |
| LIST_ENTRY *Link; | |
| EFI_KEY_DATA KeyData; | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| VIRTUAL_KBD_KEY VirtualKey; | |
| VirtualKeyboardPrivate = Context; | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| if (VirtualKeyboardPrivate->PlatformVirtual && | |
| VirtualKeyboardPrivate->PlatformVirtual->Query) | |
| { | |
| if (VirtualKeyboardPrivate->PlatformVirtual->Query (&VirtualKey) == | |
| FALSE) | |
| { | |
| goto Exit; | |
| } | |
| // Found key | |
| KeyData.Key.ScanCode = VirtualKey.Key.ScanCode; | |
| KeyData.Key.UnicodeChar = VirtualKey.Key.UnicodeChar; | |
| KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID; | |
| KeyData.KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID; | |
| if (VirtualKeyboardPrivate->PlatformVirtual->Clear) { | |
| VirtualKeyboardPrivate->PlatformVirtual->Clear (&VirtualKey); | |
| } | |
| } else { | |
| goto Exit; | |
| } | |
| // | |
| // Signal KeyNotify process event if this key pressed matches any key registered. | |
| // | |
| for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; | |
| Link != &VirtualKeyboardPrivate->NotifyList; | |
| Link = Link->ForwardLink) | |
| { | |
| CurrentNotify = CR ( | |
| Link, | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| VIRTUAL_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 (&VirtualKeyboardPrivate->QueueForNotify, &KeyData); | |
| gBS->SignalEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent); | |
| break; | |
| } | |
| } | |
| Enqueue (&VirtualKeyboardPrivate->Queue, &KeyData); | |
| Exit: | |
| // | |
| // Leave critical section and return | |
| // | |
| gBS->RestoreTPL (OldTpl); | |
| } | |
| /** | |
| 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; | |
| VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; | |
| EFI_KEY_DATA KeyData; | |
| LIST_ENTRY *Link; | |
| LIST_ENTRY *NotifyList; | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
| EFI_TPL OldTpl; | |
| VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *)Context; | |
| // | |
| // Invoke notification functions. | |
| // | |
| NotifyList = &VirtualKeyboardPrivate->NotifyList; | |
| while (TRUE) { | |
| // | |
| // Enter critical section | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| Status = Dequeue (&VirtualKeyboardPrivate->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, | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
| NotifyEntry, | |
| VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE | |
| ); | |
| if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { | |
| CurrentNotify->KeyNotificationFn (&KeyData); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| The user Entry Point for module VirtualKeyboard. 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 | |
| InitializeVirtualKeyboard ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Install driver model protocol(s). | |
| // | |
| Status = EfiLibInstallDriverBindingComponentName2 ( | |
| ImageHandle, | |
| SystemTable, | |
| &gVirtualKeyboardDriverBinding, | |
| ImageHandle, | |
| &gVirtualKeyboardComponentName, | |
| &gVirtualKeyboardComponentName2 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } |