VirtualKeyboard.c revision b09a6aae7f13aef50f08c401f981be3a196a7400
1/** @file 2 VirtualKeyboard driver 3 4Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> 5Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR> 6 7This program and the accompanying materials 8are licensed and made available under the terms and conditions 9of the BSD License which accompanies this distribution. The 10full text of the license may be found at 11http://opensource.org/licenses/bsd-license.php 12 13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 15 16**/ 17 18#include "VirtualKeyboard.h" 19 20// 21// RAM Keyboard Driver Binding Protocol Instance 22// 23EFI_DRIVER_BINDING_PROTOCOL gVirtualKeyboardDriverBinding = { 24 VirtualKeyboardDriverBindingSupported, 25 VirtualKeyboardDriverBindingStart, 26 VirtualKeyboardDriverBindingStop, 27 0x10, 28 NULL, 29 NULL 30}; 31 32// 33// EFI Driver Binding Protocol Functions 34// 35 36/** 37 Check whether the driver supports this device. 38 39 @param This The Udriver binding protocol. 40 @param Controller The controller handle to check. 41 @param RemainingDevicePath The remaining device path. 42 43 @retval EFI_SUCCESS The driver supports this controller. 44 @retval other This device isn't supported. 45 46**/ 47EFI_STATUS 48EFIAPI 49VirtualKeyboardDriverBindingSupported ( 50 IN EFI_DRIVER_BINDING_PROTOCOL *This, 51 IN EFI_HANDLE Controller, 52 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath 53 ) 54{ 55 EFI_STATUS Status; 56 PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual; 57 58 Status = gBS->OpenProtocol ( 59 Controller, 60 &gPlatformVirtualKeyboardProtocolGuid, 61 (VOID **) &PlatformVirtual, 62 This->DriverBindingHandle, 63 Controller, 64 EFI_OPEN_PROTOCOL_BY_DRIVER 65 ); 66 if (EFI_ERROR (Status)) { 67 return Status; 68 } 69 gBS->CloseProtocol ( 70 Controller, 71 &gPlatformVirtualKeyboardProtocolGuid, 72 This->DriverBindingHandle, 73 Controller 74 ); 75 return Status; 76} 77 78/** 79 Starts the device with this driver. 80 81 @param This The driver binding instance. 82 @param Controller Handle of device to bind driver to. 83 @param RemainingDevicePath Optional parameter use to pick a specific child 84 device to start. 85 86 @retval EFI_SUCCESS The controller is controlled by the driver. 87 @retval Other This controller cannot be started. 88 89**/ 90EFI_STATUS 91EFIAPI 92VirtualKeyboardDriverBindingStart ( 93 IN EFI_DRIVER_BINDING_PROTOCOL *This, 94 IN EFI_HANDLE Controller, 95 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath 96 ) 97{ 98 EFI_STATUS Status; 99 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 100 EFI_STATUS_CODE_VALUE StatusCode; 101 PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual; 102 103 Status = gBS->OpenProtocol ( 104 Controller, 105 &gPlatformVirtualKeyboardProtocolGuid, 106 (VOID **) &PlatformVirtual, 107 This->DriverBindingHandle, 108 Controller, 109 EFI_OPEN_PROTOCOL_BY_DRIVER 110 ); 111 if (EFI_ERROR (Status)) { 112 return Status; 113 } 114 115 // 116 // Allocate the private device structure 117 // 118 VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *) AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_DEV)); 119 if (NULL == VirtualKeyboardPrivate) { 120 Status = EFI_OUT_OF_RESOURCES; 121 goto Done; 122 } 123 124 // 125 // Initialize the private device structure 126 // 127 VirtualKeyboardPrivate->Signature = VIRTUAL_KEYBOARD_DEV_SIGNATURE; 128 VirtualKeyboardPrivate->Handle = Controller; 129 VirtualKeyboardPrivate->PlatformVirtual = PlatformVirtual; 130 VirtualKeyboardPrivate->Queue.Front = 0; 131 VirtualKeyboardPrivate->Queue.Rear = 0; 132 VirtualKeyboardPrivate->QueueForNotify.Front = 0; 133 VirtualKeyboardPrivate->QueueForNotify.Rear = 0; 134 135 VirtualKeyboardPrivate->SimpleTextIn.Reset = VirtualKeyboardReset; 136 VirtualKeyboardPrivate->SimpleTextIn.ReadKeyStroke = VirtualKeyboardReadKeyStroke; 137 138 VirtualKeyboardPrivate->SimpleTextInputEx.Reset = VirtualKeyboardResetEx; 139 VirtualKeyboardPrivate->SimpleTextInputEx.ReadKeyStrokeEx = VirtualKeyboardReadKeyStrokeEx; 140 VirtualKeyboardPrivate->SimpleTextInputEx.SetState = VirtualKeyboardSetState; 141 142 VirtualKeyboardPrivate->SimpleTextInputEx.RegisterKeyNotify = VirtualKeyboardRegisterKeyNotify; 143 VirtualKeyboardPrivate->SimpleTextInputEx.UnregisterKeyNotify = VirtualKeyboardUnregisterKeyNotify; 144 InitializeListHead (&VirtualKeyboardPrivate->NotifyList); 145 146 Status = PlatformVirtual->Register (); 147 if (EFI_ERROR (Status)) { 148 goto Done; 149 } 150 151 // 152 // Report that the keyboard is being enabled 153 // 154 REPORT_STATUS_CODE ( 155 EFI_PROGRESS_CODE, 156 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE 157 ); 158 159 // 160 // Setup the WaitForKey event 161 // 162 Status = gBS->CreateEvent ( 163 EVT_NOTIFY_WAIT, 164 TPL_NOTIFY, 165 VirtualKeyboardWaitForKey, 166 &(VirtualKeyboardPrivate->SimpleTextIn), 167 &((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey) 168 ); 169 if (EFI_ERROR (Status)) { 170 (VirtualKeyboardPrivate->SimpleTextIn).WaitForKey = NULL; 171 goto Done; 172 } 173 Status = gBS->CreateEvent ( 174 EVT_NOTIFY_WAIT, 175 TPL_NOTIFY, 176 VirtualKeyboardWaitForKeyEx, 177 &(VirtualKeyboardPrivate->SimpleTextInputEx), 178 &(VirtualKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx) 179 ); 180 if (EFI_ERROR (Status)) { 181 VirtualKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx = NULL; 182 goto Done; 183 } 184 185 // 186 // Setup a periodic timer, used for reading keystrokes at a fixed interval 187 // 188 Status = gBS->CreateEvent ( 189 EVT_TIMER | EVT_NOTIFY_SIGNAL, 190 TPL_NOTIFY, 191 VirtualKeyboardTimerHandler, 192 VirtualKeyboardPrivate, 193 &VirtualKeyboardPrivate->TimerEvent 194 ); 195 if (EFI_ERROR (Status)) { 196 Status = EFI_OUT_OF_RESOURCES; 197 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 198 goto Done; 199 } 200 201 Status = gBS->SetTimer ( 202 VirtualKeyboardPrivate->TimerEvent, 203 TimerPeriodic, 204 KEYBOARD_TIMER_INTERVAL 205 ); 206 if (EFI_ERROR (Status)) { 207 Status = EFI_OUT_OF_RESOURCES; 208 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 209 goto Done; 210 } 211 212 Status = gBS->CreateEvent ( 213 EVT_NOTIFY_SIGNAL, 214 TPL_CALLBACK, 215 KeyNotifyProcessHandler, 216 VirtualKeyboardPrivate, 217 &VirtualKeyboardPrivate->KeyNotifyProcessEvent 218 ); 219 if (EFI_ERROR (Status)) { 220 Status = EFI_OUT_OF_RESOURCES; 221 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 222 goto Done; 223 } 224 225 // 226 // Report a Progress Code for an attempt to detect the precense of the keyboard device in the system 227 // 228 REPORT_STATUS_CODE ( 229 EFI_PROGRESS_CODE, 230 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT 231 ); 232 233 // 234 // Reset the keyboard device 235 // 236 Status = VirtualKeyboardPrivate->SimpleTextInputEx.Reset ( 237 &VirtualKeyboardPrivate->SimpleTextInputEx, 238 FALSE 239 ); 240 if (EFI_ERROR (Status)) { 241 DEBUG ((EFI_D_ERROR, "[KBD]Reset Failed. Status - %r\n", Status)); 242 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED; 243 goto Done; 244 } 245 // 246 // Install protocol interfaces for the keyboard device. 247 // 248 Status = gBS->InstallMultipleProtocolInterfaces ( 249 &Controller, 250 &gEfiSimpleTextInProtocolGuid, 251 &VirtualKeyboardPrivate->SimpleTextIn, 252 &gEfiSimpleTextInputExProtocolGuid, 253 &VirtualKeyboardPrivate->SimpleTextInputEx, 254 NULL 255 ); 256 257Done: 258 if (StatusCode != 0) { 259 // 260 // Report an Error Code for failing to start the keyboard device 261 // 262 REPORT_STATUS_CODE ( 263 EFI_ERROR_CODE | EFI_ERROR_MINOR, 264 StatusCode 265 ); 266 } 267 268 if (EFI_ERROR (Status)) { 269 if (VirtualKeyboardPrivate != NULL) { 270 if ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey != NULL) { 271 gBS->CloseEvent ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey); 272 } 273 274 if ((VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx != NULL) { 275 gBS->CloseEvent ((VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx); 276 } 277 278 if (VirtualKeyboardPrivate->KeyNotifyProcessEvent != NULL) { 279 gBS->CloseEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent); 280 } 281 282 VirtualKeyboardFreeNotifyList (&VirtualKeyboardPrivate->NotifyList); 283 284 if (VirtualKeyboardPrivate->TimerEvent != NULL) { 285 gBS->CloseEvent (VirtualKeyboardPrivate->TimerEvent); 286 } 287 FreePool (VirtualKeyboardPrivate); 288 } 289 } 290 291 gBS->CloseProtocol ( 292 Controller, 293 &gPlatformVirtualKeyboardProtocolGuid, 294 This->DriverBindingHandle, 295 Controller 296 ); 297 298 return Status; 299} 300 301/** 302 Stop the device handled by this driver. 303 304 @param This The driver binding protocol. 305 @param Controller The controller to release. 306 @param NumberOfChildren The number of handles in ChildHandleBuffer. 307 @param ChildHandleBuffer The array of child handle. 308 309 @retval EFI_SUCCESS The device was stopped. 310 @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. 311 @retval Others Fail to uninstall protocols attached on the device. 312 313**/ 314EFI_STATUS 315EFIAPI 316VirtualKeyboardDriverBindingStop ( 317 IN EFI_DRIVER_BINDING_PROTOCOL *This, 318 IN EFI_HANDLE Controller, 319 IN UINTN NumberOfChildren, 320 IN EFI_HANDLE *ChildHandleBuffer 321 ) 322{ 323 return EFI_SUCCESS; 324} 325 326 327/** 328 Enqueue the key. 329 330 @param Queue The queue to be enqueued. 331 @param KeyData The key data to be enqueued. 332 333 @retval EFI_NOT_READY The queue is full. 334 @retval EFI_SUCCESS Successfully enqueued the key data. 335 336**/ 337EFI_STATUS 338Enqueue ( 339 IN SIMPLE_QUEUE *Queue, 340 IN EFI_KEY_DATA *KeyData 341 ) 342{ 343 if ((Queue->Rear + 1) % QUEUE_MAX_COUNT == Queue->Front) { 344 return EFI_NOT_READY; 345 } 346 347 CopyMem (&Queue->Buffer[Queue->Rear], KeyData, sizeof (EFI_KEY_DATA)); 348 Queue->Rear = (Queue->Rear + 1) % QUEUE_MAX_COUNT; 349 350 return EFI_SUCCESS; 351} 352 353/** 354 Dequeue the key. 355 356 @param Queue The queue to be dequeued. 357 @param KeyData The key data to be dequeued. 358 359 @retval EFI_NOT_READY The queue is empty. 360 @retval EFI_SUCCESS Successfully dequeued the key data. 361 362**/ 363EFI_STATUS 364Dequeue ( 365 IN SIMPLE_QUEUE *Queue, 366 IN EFI_KEY_DATA *KeyData 367 ) 368{ 369 if (Queue->Front == Queue->Rear) { 370 return EFI_NOT_READY; 371 } 372 373 CopyMem (KeyData, &Queue->Buffer[Queue->Front], sizeof (EFI_KEY_DATA)); 374 Queue->Front = (Queue->Front + 1) % QUEUE_MAX_COUNT; 375 376 return EFI_SUCCESS; 377} 378 379/** 380 Check whether the queue is empty. 381 382 @param Queue The queue to be checked. 383 384 @retval EFI_NOT_READY The queue is empty. 385 @retval EFI_SUCCESS The queue is not empty. 386 387**/ 388EFI_STATUS 389CheckQueue ( 390 IN SIMPLE_QUEUE *Queue 391 ) 392{ 393 if (Queue->Front == Queue->Rear) { 394 return EFI_NOT_READY; 395 } 396 397 return EFI_SUCCESS; 398} 399 400/** 401 Check key buffer to get the key stroke status. 402 403 @param This Pointer of the protocol EFI_SIMPLE_TEXT_IN_PROTOCOL. 404 405 @retval EFI_SUCCESS A key is being pressed now. 406 @retval Other No key is now pressed. 407 408**/ 409EFI_STATUS 410EFIAPI 411VirtualKeyboardCheckForKey ( 412 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This 413 ) 414{ 415 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 416 417 VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); 418 419 return CheckQueue (&VirtualKeyboardPrivate->Queue); 420} 421 422/** 423 Free keyboard notify list. 424 425 @param ListHead The list head 426 427 @retval EFI_SUCCESS Free the notify list successfully 428 @retval EFI_INVALID_PARAMETER ListHead is invalid. 429 430**/ 431EFI_STATUS 432VirtualKeyboardFreeNotifyList ( 433 IN OUT LIST_ENTRY *ListHead 434 ) 435{ 436 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; 437 438 if (ListHead == NULL) { 439 return EFI_INVALID_PARAMETER; 440 } 441 while (!IsListEmpty (ListHead)) { 442 NotifyNode = CR ( 443 ListHead->ForwardLink, 444 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, 445 NotifyEntry, 446 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE 447 ); 448 RemoveEntryList (ListHead->ForwardLink); 449 gBS->FreePool (NotifyNode); 450 } 451 452 return EFI_SUCCESS; 453} 454 455/** 456 Judge whether is a registed key 457 458 @param RegsiteredData A pointer to a buffer that is filled in with the keystroke 459 state data for the key that was registered. 460 @param InputData A pointer to a buffer that is filled in with the keystroke 461 state data for the key that was pressed. 462 463 @retval TRUE Key be pressed matches a registered key. 464 @retval FLASE Match failed. 465 466**/ 467BOOLEAN 468IsKeyRegistered ( 469 IN EFI_KEY_DATA *RegsiteredData, 470 IN EFI_KEY_DATA *InputData 471 ) 472 473{ 474 ASSERT (RegsiteredData != NULL && InputData != NULL); 475 476 if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) || 477 (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) { 478 return FALSE; 479 } 480 481 // 482 // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored. 483 // 484 if (RegsiteredData->KeyState.KeyShiftState != 0 && 485 RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState) { 486 return FALSE; 487 } 488 if (RegsiteredData->KeyState.KeyToggleState != 0 && 489 RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState) { 490 return FALSE; 491 } 492 493 return TRUE; 494 495} 496 497/** 498 Event notification function for SIMPLE_TEXT_IN.WaitForKey event 499 Signal the event if there is key available 500 501 @param Event the event object 502 @param Context waitting context 503 504**/ 505VOID 506EFIAPI 507VirtualKeyboardWaitForKey ( 508 IN EFI_EVENT Event, 509 IN VOID *Context 510 ) 511{ 512 // 513 // Stall 1ms to give a chance to let other driver interrupt this routine for their timer event. 514 // Csm will be used to check whether there is a key pending, but the csm will disable all 515 // interrupt before switch to compatibility16, which mean all the efiCompatibility timer 516 // event will stop work during the compatibility16. And If a caller recursivly invoke this function, 517 // e.g. UI setup or Shell, other drivers which are driven by timer event will have a bad performance during this period, 518 // e.g. usb keyboard driver. 519 // Add a stall period can greatly increate other driver performance during the WaitForKey is recursivly invoked. 520 // 1ms delay will make little impact to the thunk keyboard driver, and user can not feel the delay at all when input. 521 // 522 gBS->Stall (1000); 523 // 524 // Use TimerEvent callback function to check whether there's any key pressed 525 // 526 VirtualKeyboardTimerHandler (NULL, VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context)); 527 528 if (!EFI_ERROR (VirtualKeyboardCheckForKey (Context))) { 529 gBS->SignalEvent (Event); 530 } 531} 532 533/** 534 Event notification function for SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event 535 Signal the event if there is key available 536 537 @param Event event object 538 @param Context waiting context 539 540**/ 541VOID 542EFIAPI 543VirtualKeyboardWaitForKeyEx ( 544 IN EFI_EVENT Event, 545 IN VOID *Context 546 ) 547 548{ 549 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 550 551 VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context); 552 VirtualKeyboardWaitForKey (Event, &VirtualKeyboardPrivate->SimpleTextIn); 553 554} 555 556// 557// EFI Simple Text In Protocol Functions 558// 559/** 560 Reset the Keyboard and do BAT test for it, if (ExtendedVerification == TRUE) then do some extra keyboard validations. 561 562 @param This Pointer of simple text Protocol. 563 @param ExtendedVerification Whether perform the extra validation of keyboard. True: perform; FALSE: skip. 564 565 @retval EFI_SUCCESS The command byte is written successfully. 566 @retval EFI_DEVICE_ERROR Errors occurred during resetting keyboard. 567 568**/ 569EFI_STATUS 570EFIAPI 571VirtualKeyboardReset ( 572 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, 573 IN BOOLEAN ExtendedVerification 574 ) 575{ 576 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 577 EFI_STATUS Status; 578 EFI_TPL OldTpl; 579 580 VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); 581 582 // 583 // Raise TPL to avoid mouse operation impact 584 // 585 OldTpl = gBS->RaiseTPL (TPL_NOTIFY); 586 587 if (VirtualKeyboardPrivate->PlatformVirtual && VirtualKeyboardPrivate->PlatformVirtual->Reset) { 588 Status = VirtualKeyboardPrivate->PlatformVirtual->Reset (); 589 } else { 590 Status = EFI_INVALID_PARAMETER; 591 } 592 593 // 594 // resume priority of task level 595 // 596 gBS->RestoreTPL (OldTpl); 597 598 return Status; 599} 600 601/** 602 Reset the input device and optionaly run diagnostics 603 604 @param This Protocol instance pointer. 605 @param ExtendedVerification Driver may perform diagnostics on reset. 606 607 @retval EFI_SUCCESS The device was reset. 608 @retval EFI_DEVICE_ERROR The device is not functioning properly and could- 609 not be reset. 610 611**/ 612EFI_STATUS 613EFIAPI 614VirtualKeyboardResetEx ( 615 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, 616 IN BOOLEAN ExtendedVerification 617 ) 618{ 619 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 620 EFI_STATUS Status; 621 EFI_TPL OldTpl; 622 623 VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); 624 625 Status = VirtualKeyboardPrivate->SimpleTextIn.Reset ( 626 &VirtualKeyboardPrivate->SimpleTextIn, 627 ExtendedVerification 628 ); 629 if (EFI_ERROR (Status)) { 630 return EFI_DEVICE_ERROR; 631 } 632 633 OldTpl = gBS->RaiseTPL (TPL_NOTIFY); 634 635 gBS->RestoreTPL (OldTpl); 636 637 return EFI_SUCCESS; 638 639} 640 641/** 642 Reads the next keystroke from the input device. The WaitForKey Event can 643 be used to test for existance of a keystroke via WaitForEvent () call. 644 645 @param VirtualKeyboardPrivate Virtualkeyboard driver private structure. 646 @param KeyData A pointer to a buffer that is filled in with the keystroke 647 state data for the key that was pressed. 648 649 @retval EFI_SUCCESS The keystroke information was returned. 650 @retval EFI_NOT_READY There was no keystroke data availiable. 651 @retval EFI_DEVICE_ERROR The keystroke information was not returned due to 652 hardware errors. 653 @retval EFI_INVALID_PARAMETER KeyData is NULL. 654 655**/ 656EFI_STATUS 657KeyboardReadKeyStrokeWorker ( 658 IN VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate, 659 OUT EFI_KEY_DATA *KeyData 660 ) 661{ 662 EFI_STATUS Status; 663 EFI_TPL OldTpl; 664 if (KeyData == NULL) { 665 return EFI_INVALID_PARAMETER; 666 } 667 668 // 669 // Use TimerEvent callback function to check whether there's any key pressed 670 // 671 672 // 673 // Stall 1ms to give a chance to let other driver interrupt this routine for their timer event. 674 // Csm will be used to check whether there is a key pending, but the csm will disable all 675 // interrupt before switch to compatibility16, which mean all the efiCompatibility timer 676 // event will stop work during the compatibility16. And If a caller recursivly invoke this function, 677 // e.g. OS loader, other drivers which are driven by timer event will have a bad performance during this period, 678 // e.g. usb keyboard driver. 679 // Add a stall period can greatly increate other driver performance during the WaitForKey is recursivly invoked. 680 // 1ms delay will make little impact to the thunk keyboard driver, and user can not feel the delay at all when input. 681 // 682 gBS->Stall (1000); 683 684 OldTpl = gBS->RaiseTPL (TPL_NOTIFY); 685 686 VirtualKeyboardTimerHandler (NULL, VirtualKeyboardPrivate); 687 // 688 // If there's no key, just return 689 // 690 Status = CheckQueue (&VirtualKeyboardPrivate->Queue); 691 if (EFI_ERROR (Status)) { 692 gBS->RestoreTPL (OldTpl); 693 return EFI_NOT_READY; 694 } 695 696 Status = Dequeue (&VirtualKeyboardPrivate->Queue, KeyData); 697 698 gBS->RestoreTPL (OldTpl); 699 700 return EFI_SUCCESS; 701} 702 703/** 704 Read out the scan code of the key that has just been stroked. 705 706 @param This Pointer of simple text Protocol. 707 @param Key Pointer for store the key that read out. 708 709 @retval EFI_SUCCESS The key is read out successfully. 710 @retval other The key reading failed. 711 712**/ 713EFI_STATUS 714EFIAPI 715VirtualKeyboardReadKeyStroke ( 716 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, 717 OUT EFI_INPUT_KEY *Key 718 ) 719{ 720 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 721 EFI_STATUS Status; 722 EFI_KEY_DATA KeyData; 723 724 VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); 725 726 Status = KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, &KeyData); 727 if (EFI_ERROR (Status)) { 728 return Status; 729 } 730 731 // 732 // Convert the Ctrl+[a-z] to Ctrl+[1-26] 733 // 734 if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) { 735 if (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') { 736 KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'a' + 1); 737 } else if (KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') { 738 KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'A' + 1); 739 } 740 } 741 742 CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); 743 744 return EFI_SUCCESS; 745} 746 747/** 748 Reads the next keystroke from the input device. The WaitForKey Event can 749 be used to test for existance of a keystroke via WaitForEvent () call. 750 751 @param This Protocol instance pointer. 752 @param KeyData A pointer to a buffer that is filled in with the keystroke 753 state data for the key that was pressed. 754 755 @retval EFI_SUCCESS The keystroke information was returned. 756 @retval EFI_NOT_READY There was no keystroke data availiable. 757 @retval EFI_DEVICE_ERROR The keystroke information was not returned due to 758 hardware errors. 759 @retval EFI_INVALID_PARAMETER KeyData is NULL. 760 761**/ 762EFI_STATUS 763EFIAPI 764VirtualKeyboardReadKeyStrokeEx ( 765 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, 766 OUT EFI_KEY_DATA *KeyData 767 ) 768{ 769 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 770 771 if (KeyData == NULL) { 772 return EFI_INVALID_PARAMETER; 773 } 774 775 VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); 776 777 return KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, KeyData); 778 779} 780 781/** 782 Set certain state for the input device. 783 784 @param This Protocol instance pointer. 785 @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the- 786 state for the input device. 787 788 @retval EFI_SUCCESS The device state was set successfully. 789 @retval EFI_DEVICE_ERROR The device is not functioning correctly and could- 790 not have the setting adjusted. 791 @retval EFI_UNSUPPORTED The device does not have the ability to set its state. 792 @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. 793 794**/ 795EFI_STATUS 796EFIAPI 797VirtualKeyboardSetState ( 798 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, 799 IN EFI_KEY_TOGGLE_STATE *KeyToggleState 800 ) 801{ 802 if (KeyToggleState == NULL) { 803 return EFI_INVALID_PARAMETER; 804 } 805 806 // 807 // Thunk keyboard driver doesn't support partial keystroke. 808 // 809 if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID || 810 (*KeyToggleState & EFI_KEY_STATE_EXPOSED) == EFI_KEY_STATE_EXPOSED 811 ) { 812 return EFI_UNSUPPORTED; 813 } 814 815 return EFI_SUCCESS; 816} 817 818/** 819 Register a notification function for a particular keystroke for the input device. 820 821 @param This Protocol instance pointer. 822 @param KeyData A pointer to a buffer that is filled in with the keystroke 823 information data for the key that was pressed. 824 @param KeyNotificationFunction Points to the function to be called when the key 825 sequence is typed specified by KeyData. 826 @param NotifyHandle Points to the unique handle assigned to the registered notification. 827 828 829 @retval EFI_SUCCESS The notification function was registered successfully. 830 @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data structures. 831 @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. 832 833**/ 834EFI_STATUS 835EFIAPI 836VirtualKeyboardRegisterKeyNotify ( 837 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, 838 IN EFI_KEY_DATA *KeyData, 839 IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, 840 OUT VOID **NotifyHandle 841 ) 842{ 843 EFI_STATUS Status; 844 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 845 EFI_TPL OldTpl; 846 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify; 847 LIST_ENTRY *Link; 848 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; 849 850 if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { 851 return EFI_INVALID_PARAMETER; 852 } 853 854 VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); 855 856 // 857 // Enter critical section 858 // 859 OldTpl = gBS->RaiseTPL (TPL_NOTIFY); 860 861 // 862 // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. 863 // 864 for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; Link != &VirtualKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { 865 CurrentNotify = CR ( 866 Link, 867 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, 868 NotifyEntry, 869 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE 870 ); 871 if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { 872 if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { 873 *NotifyHandle = CurrentNotify; 874 Status = EFI_SUCCESS; 875 goto Exit; 876 } 877 } 878 } 879 880 // 881 // Allocate resource to save the notification function 882 // 883 884 NewNotify = (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY)); 885 if (NewNotify == NULL) { 886 Status = EFI_OUT_OF_RESOURCES; 887 goto Exit; 888 } 889 890 NewNotify->Signature = VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE; 891 NewNotify->KeyNotificationFn = KeyNotificationFunction; 892 CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); 893 InsertTailList (&VirtualKeyboardPrivate->NotifyList, &NewNotify->NotifyEntry); 894 895 *NotifyHandle = NewNotify; 896 Status = EFI_SUCCESS; 897 898Exit: 899 // 900 // Leave critical section and return 901 // 902 gBS->RestoreTPL (OldTpl); 903 return Status; 904 905} 906 907/** 908 Remove a registered notification function from a particular keystroke. 909 910 @param This Protocol instance pointer. 911 @param NotificationHandle The handle of the notification function being unregistered. 912 913 @retval EFI_SUCCESS The notification function was unregistered successfully. 914 @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. 915 916**/ 917EFI_STATUS 918EFIAPI 919VirtualKeyboardUnregisterKeyNotify ( 920 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, 921 IN VOID *NotificationHandle 922 ) 923{ 924 EFI_STATUS Status; 925 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 926 EFI_TPL OldTpl; 927 LIST_ENTRY *Link; 928 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; 929 930 // 931 // Check incoming notification handle 932 // 933 if (NotificationHandle == NULL) { 934 return EFI_INVALID_PARAMETER; 935 } 936 937 if (((VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) NotificationHandle)->Signature != VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) { 938 return EFI_INVALID_PARAMETER; 939 } 940 941 VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); 942 943 // 944 // Enter critical section 945 // 946 OldTpl = gBS->RaiseTPL (TPL_NOTIFY); 947 948 for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; Link != &VirtualKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { 949 CurrentNotify = CR ( 950 Link, 951 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, 952 NotifyEntry, 953 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE 954 ); 955 if (CurrentNotify == NotificationHandle) { 956 // 957 // Remove the notification function from NotifyList and free resources 958 // 959 RemoveEntryList (&CurrentNotify->NotifyEntry); 960 961 Status = EFI_SUCCESS; 962 goto Exit; 963 } 964 } 965 966 // 967 // Can not find the specified Notification Handle 968 // 969 Status = EFI_INVALID_PARAMETER; 970 971Exit: 972 // 973 // Leave critical section and return 974 // 975 gBS->RestoreTPL (OldTpl); 976 return Status; 977} 978 979/** 980 Timer event handler: read a series of scancodes from 8042 981 and put them into memory scancode buffer. 982 it read as much scancodes to either fill 983 the memory buffer or empty the keyboard buffer. 984 It is registered as running under TPL_NOTIFY 985 986 @param Event The timer event 987 @param Context A KEYBOARD_CONSOLE_IN_DEV pointer 988 989**/ 990VOID 991EFIAPI 992VirtualKeyboardTimerHandler ( 993 IN EFI_EVENT Event, 994 IN VOID *Context 995 ) 996{ 997 EFI_STATUS Status; 998 EFI_TPL OldTpl; 999 LIST_ENTRY *Link; 1000 EFI_KEY_DATA KeyData; 1001 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; 1002 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 1003 VIRTUAL_KBD_KEY VirtualKey; 1004 1005 VirtualKeyboardPrivate = Context; 1006 1007 // 1008 // Enter critical section 1009 // 1010 OldTpl = gBS->RaiseTPL (TPL_NOTIFY); 1011 1012 if (VirtualKeyboardPrivate->PlatformVirtual && 1013 VirtualKeyboardPrivate->PlatformVirtual->Query) { 1014 if (VirtualKeyboardPrivate->PlatformVirtual->Query (&VirtualKey) == FALSE) { 1015 Status = EFI_INVALID_PARAMETER; 1016 goto Exit; 1017 } 1018 // Found key 1019 KeyData.Key.ScanCode = VirtualKey.Key.ScanCode; 1020 KeyData.Key.UnicodeChar = VirtualKey.Key.UnicodeChar; 1021 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID; 1022 KeyData.KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID; 1023 if (VirtualKeyboardPrivate->PlatformVirtual->Clear) { 1024 VirtualKeyboardPrivate->PlatformVirtual->Clear (&VirtualKey); 1025 } 1026 } else { 1027 Status = EFI_INVALID_PARAMETER; 1028 goto Exit; 1029 } 1030 1031 // 1032 // Signal KeyNotify process event if this key pressed matches any key registered. 1033 // 1034 for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; Link != &VirtualKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { 1035 CurrentNotify = CR ( 1036 Link, 1037 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, 1038 NotifyEntry, 1039 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE 1040 ); 1041 if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { 1042 // 1043 // The key notification function needs to run at TPL_CALLBACK 1044 // while current TPL is TPL_NOTIFY. It will be invoked in 1045 // KeyNotifyProcessHandler() which runs at TPL_CALLBACK. 1046 // 1047 Enqueue (&VirtualKeyboardPrivate->QueueForNotify, &KeyData); 1048 gBS->SignalEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent); 1049 } 1050 } 1051 1052 Enqueue (&VirtualKeyboardPrivate->Queue, &KeyData); 1053 1054Exit: 1055 // 1056 // Leave critical section and return 1057 // 1058 gBS->RestoreTPL (OldTpl); 1059} 1060 1061/** 1062 Process key notify. 1063 1064 @param Event Indicates the event that invoke this function. 1065 @param Context Indicates the calling context. 1066**/ 1067VOID 1068EFIAPI 1069KeyNotifyProcessHandler ( 1070 IN EFI_EVENT Event, 1071 IN VOID *Context 1072 ) 1073{ 1074 EFI_STATUS Status; 1075 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; 1076 EFI_KEY_DATA KeyData; 1077 LIST_ENTRY *Link; 1078 LIST_ENTRY *NotifyList; 1079 VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; 1080 EFI_TPL OldTpl; 1081 1082 VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *) Context; 1083 1084 // 1085 // Invoke notification functions. 1086 // 1087 NotifyList = &VirtualKeyboardPrivate->NotifyList; 1088 while (TRUE) { 1089 // 1090 // Enter critical section 1091 // 1092 OldTpl = gBS->RaiseTPL (TPL_NOTIFY); 1093 Status = Dequeue (&VirtualKeyboardPrivate->QueueForNotify, &KeyData); 1094 // 1095 // Leave critical section 1096 // 1097 gBS->RestoreTPL (OldTpl); 1098 if (EFI_ERROR (Status)) { 1099 break; 1100 } 1101 for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) { 1102 CurrentNotify = CR (Link, VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, NotifyEntry, VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE); 1103 if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { 1104 CurrentNotify->KeyNotificationFn (&KeyData); 1105 } 1106 } 1107 } 1108} 1109 1110/** 1111 The user Entry Point for module VirtualKeyboard. The user code starts with this function. 1112 1113 @param[in] ImageHandle The firmware allocated handle for the EFI image. 1114 @param[in] SystemTable A pointer to the EFI System Table. 1115 1116 @retval EFI_SUCCESS The entry point is executed successfully. 1117 @retval other Some error occurs when executing this entry point. 1118 1119**/ 1120EFI_STATUS 1121EFIAPI 1122InitializeVirtualKeyboard( 1123 IN EFI_HANDLE ImageHandle, 1124 IN EFI_SYSTEM_TABLE *SystemTable 1125 ) 1126{ 1127 EFI_STATUS Status; 1128 1129 // 1130 // Install driver model protocol(s). 1131 // 1132 Status = EfiLibInstallDriverBindingComponentName2 ( 1133 ImageHandle, 1134 SystemTable, 1135 &gVirtualKeyboardDriverBinding, 1136 ImageHandle, 1137 &gVirtualKeyboardComponentName, 1138 &gVirtualKeyboardComponentName2 1139 ); 1140 ASSERT_EFI_ERROR (Status); 1141 1142 return Status; 1143} 1144