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