1 /** @file
2   Routines implements SIMPLE_TEXT_IN protocol's interfaces based on 8042 interfaces
3   provided by Ps2KbdCtrller.c.
4 
5 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 
11 #include "Ps2Keyboard.h"
12 
13 /**
14   Check whether the EFI key buffer is empty.
15 
16   @param Queue     Pointer to instance of EFI_KEY_QUEUE.
17 
18   @retval TRUE    The EFI key buffer is empty.
19   @retval FALSE   The EFI key buffer isn't empty.
20 **/
21 BOOLEAN
IsEfikeyBufEmpty(IN EFI_KEY_QUEUE * Queue)22 IsEfikeyBufEmpty (
23   IN  EFI_KEY_QUEUE         *Queue
24   )
25 {
26   return (BOOLEAN) (Queue->Head == Queue->Tail);
27 }
28 
29 /**
30   Read & remove one key data from the EFI key buffer.
31 
32   @param Queue     Pointer to instance of EFI_KEY_QUEUE.
33   @param KeyData   Receive the key data.
34 
35   @retval EFI_SUCCESS   The key data is popped successfully.
36   @retval EFI_NOT_READY There is no key data available.
37 **/
38 EFI_STATUS
PopEfikeyBufHead(IN EFI_KEY_QUEUE * Queue,OUT EFI_KEY_DATA * KeyData OPTIONAL)39 PopEfikeyBufHead (
40   IN  EFI_KEY_QUEUE         *Queue,
41   OUT EFI_KEY_DATA          *KeyData OPTIONAL
42   )
43 {
44   if (IsEfikeyBufEmpty (Queue)) {
45     return EFI_NOT_READY;
46   }
47   //
48   // Retrieve and remove the values
49   //
50   if (KeyData != NULL) {
51     CopyMem (KeyData, &Queue->Buffer[Queue->Head], sizeof (EFI_KEY_DATA));
52   }
53   Queue->Head = (Queue->Head + 1) % KEYBOARD_EFI_KEY_MAX_COUNT;
54   return EFI_SUCCESS;
55 }
56 
57 /**
58   Push one key data to the EFI key buffer.
59 
60   @param Queue     Pointer to instance of EFI_KEY_QUEUE.
61   @param KeyData   The key data to push.
62 **/
63 VOID
PushEfikeyBufTail(IN EFI_KEY_QUEUE * Queue,IN EFI_KEY_DATA * KeyData)64 PushEfikeyBufTail (
65   IN  EFI_KEY_QUEUE         *Queue,
66   IN  EFI_KEY_DATA          *KeyData
67   )
68 {
69   if ((Queue->Tail + 1) % KEYBOARD_EFI_KEY_MAX_COUNT == Queue->Head) {
70     //
71     // If Queue is full, pop the one from head.
72     //
73     PopEfikeyBufHead (Queue, NULL);
74   }
75   CopyMem (&Queue->Buffer[Queue->Tail], KeyData, sizeof (EFI_KEY_DATA));
76   Queue->Tail = (Queue->Tail + 1) % KEYBOARD_EFI_KEY_MAX_COUNT;
77 }
78 
79 /**
80   Judge whether is a registed key
81 
82   @param RegsiteredData       A pointer to a buffer that is filled in with the keystroke
83                               state data for the key that was registered.
84   @param InputData            A pointer to a buffer that is filled in with the keystroke
85                               state data for the key that was pressed.
86 
87   @retval TRUE                Key be pressed matches a registered key.
88   @retval FLASE               Match failed.
89 
90 **/
91 BOOLEAN
IsKeyRegistered(IN EFI_KEY_DATA * RegsiteredData,IN EFI_KEY_DATA * InputData)92 IsKeyRegistered (
93   IN EFI_KEY_DATA  *RegsiteredData,
94   IN EFI_KEY_DATA  *InputData
95   )
96 
97 {
98   ASSERT (RegsiteredData != NULL && InputData != NULL);
99 
100   if ((RegsiteredData->Key.ScanCode    != InputData->Key.ScanCode) ||
101       (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) {
102     return FALSE;
103   }
104 
105   //
106   // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored.
107   //
108   if (RegsiteredData->KeyState.KeyShiftState != 0 &&
109       RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState) {
110     return FALSE;
111   }
112   if (RegsiteredData->KeyState.KeyToggleState != 0 &&
113       RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState) {
114     return FALSE;
115   }
116 
117   return TRUE;
118 
119 }
120 
121 /**
122     Reads the next keystroke from the input device. The WaitForKey Event can
123     be used to test for existance of a keystroke via WaitForEvent () call.
124 
125     @param ConsoleInDev          Ps2 Keyboard private structure
126     @param KeyData               A pointer to a buffer that is filled in with the keystroke
127                                  state data for the key that was pressed.
128 
129 
130     @retval EFI_SUCCESS             The keystroke information was returned.
131     @retval EFI_NOT_READY           There was no keystroke data availiable.
132     @retval EFI_DEVICE_ERROR        The keystroke information was not returned due to
133                                     hardware errors.
134     @retval EFI_INVALID_PARAMETER   KeyData is NULL.
135 
136 **/
137 EFI_STATUS
KeyboardReadKeyStrokeWorker(IN KEYBOARD_CONSOLE_IN_DEV * ConsoleInDev,OUT EFI_KEY_DATA * KeyData)138 KeyboardReadKeyStrokeWorker (
139   IN  KEYBOARD_CONSOLE_IN_DEV           *ConsoleInDev,
140   OUT EFI_KEY_DATA                      *KeyData
141   )
142 
143 {
144   EFI_STATUS                            Status;
145   EFI_TPL                               OldTpl;
146 
147   if (KeyData == NULL) {
148     return EFI_INVALID_PARAMETER;
149   }
150 
151   //
152   // Enter critical section
153   //
154   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
155 
156   KeyboardTimerHandler (NULL, ConsoleInDev);
157 
158   if (ConsoleInDev->KeyboardErr) {
159     Status = EFI_DEVICE_ERROR;
160   } else {
161     Status = PopEfikeyBufHead (&ConsoleInDev->EfiKeyQueue, KeyData);
162     if (Status == EFI_NOT_READY) {
163       ZeroMem (&KeyData->Key, sizeof (KeyData->Key));
164       InitializeKeyState (ConsoleInDev, &KeyData->KeyState);
165     }
166   }
167 
168   gBS->RestoreTPL (OldTpl);
169   return Status;
170 }
171 
172 /**
173   Perform 8042 controller and keyboard initialization which implement SIMPLE_TEXT_IN.Reset()
174 
175   @param This                 Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL
176   @param ExtendedVerification Indicate that the driver may perform a more
177                               exhaustive verification operation of the device during
178                               reset, now this par is ignored in this driver
179 
180 **/
181 EFI_STATUS
182 EFIAPI
KeyboardEfiReset(IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL * This,IN BOOLEAN ExtendedVerification)183 KeyboardEfiReset (
184   IN  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
185   IN  BOOLEAN                         ExtendedVerification
186   )
187 {
188   EFI_STATUS              Status;
189   KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
190   EFI_TPL                 OldTpl;
191 
192   ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
193   if (ConsoleIn->KeyboardErr) {
194     return EFI_DEVICE_ERROR;
195   }
196 
197   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
198     EFI_PROGRESS_CODE,
199     EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_RESET,
200     ConsoleIn->DevicePath
201     );
202 
203   //
204   // Enter critical section
205   //
206   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
207 
208   //
209   // Call InitKeyboard to initialize the keyboard
210   //
211   Status = InitKeyboard (ConsoleIn, ExtendedVerification);
212   if (EFI_ERROR (Status)) {
213     //
214     // Leave critical section and return
215     //
216     gBS->RestoreTPL (OldTpl);
217     return EFI_DEVICE_ERROR;
218   }
219 
220   //
221   // Leave critical section and return
222   //
223   gBS->RestoreTPL (OldTpl);
224 
225   //
226   // Report the status If a stuck key was detected
227   //
228   if (KeyReadStatusRegister (ConsoleIn) & 0x01) {
229     REPORT_STATUS_CODE_WITH_DEVICE_PATH (
230       EFI_ERROR_CODE | EFI_ERROR_MINOR,
231       EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_EC_STUCK_KEY,
232       ConsoleIn->DevicePath
233       );
234   }
235   //
236   // Report the status If keyboard is locked
237   //
238   if ((KeyReadStatusRegister (ConsoleIn) & 0x10) == 0) {
239     REPORT_STATUS_CODE_WITH_DEVICE_PATH (
240       EFI_ERROR_CODE | EFI_ERROR_MINOR,
241       EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_EC_LOCKED,
242       ConsoleIn->DevicePath
243       );
244   }
245 
246   return EFI_SUCCESS;
247 }
248 
249 /**
250   Retrieve key values for driver user which implement SIMPLE_TEXT_IN.ReadKeyStroke().
251 
252   @param This    Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL
253   @param Key     The output buffer for key value
254 
255   @retval EFI_SUCCESS success to read key stroke
256 **/
257 EFI_STATUS
258 EFIAPI
KeyboardReadKeyStroke(IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL * This,OUT EFI_INPUT_KEY * Key)259 KeyboardReadKeyStroke (
260   IN  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
261   OUT EFI_INPUT_KEY                   *Key
262   )
263 {
264   EFI_STATUS              Status;
265   KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
266   EFI_KEY_DATA            KeyData;
267 
268   ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
269 
270   //
271   // Considering if the partial keystroke is enabled, there maybe a partial
272   // keystroke in the queue, so here skip the partial keystroke and get the
273   // next key from the queue
274   //
275   while (1) {
276     //
277     // If there is no pending key, then return.
278     //
279     Status = KeyboardReadKeyStrokeWorker (ConsoleIn, &KeyData);
280     if (EFI_ERROR (Status)) {
281       return Status;
282     }
283     //
284     // If it is partial keystroke, skip it.
285     //
286     if (KeyData.Key.ScanCode == SCAN_NULL && KeyData.Key.UnicodeChar == CHAR_NULL) {
287       continue;
288     }
289     //
290     // Translate the CTRL-Alpha characters to their corresponding control value
291     // (ctrl-a = 0x0001 through ctrl-Z = 0x001A)
292     //
293     if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) {
294       if (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') {
295         KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'a' + 1);
296       } else if (KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') {
297         KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'A' + 1);
298       }
299     }
300 
301     CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY));
302     return EFI_SUCCESS;
303   }
304 }
305 
306 /**
307   Event notification function for SIMPLE_TEXT_IN.WaitForKey event
308   Signal the event if there is key available
309 
310   @param Event    the event object
311   @param Context  waitting context
312 
313 **/
314 VOID
315 EFIAPI
KeyboardWaitForKey(IN EFI_EVENT Event,IN VOID * Context)316 KeyboardWaitForKey (
317   IN  EFI_EVENT               Event,
318   IN  VOID                    *Context
319   )
320 {
321   EFI_TPL                     OldTpl;
322   KEYBOARD_CONSOLE_IN_DEV     *ConsoleIn;
323   EFI_KEY_DATA                KeyData;
324 
325   ConsoleIn = (KEYBOARD_CONSOLE_IN_DEV *) Context;
326 
327   //
328   // Enter critical section
329   //
330   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
331 
332   KeyboardTimerHandler (NULL, ConsoleIn);
333 
334   if (!ConsoleIn->KeyboardErr) {
335     //
336     // WaitforKey doesn't suppor the partial key.
337     // Considering if the partial keystroke is enabled, there maybe a partial
338     // keystroke in the queue, so here skip the partial keystroke and get the
339     // next key from the queue
340     //
341     while (!IsEfikeyBufEmpty (&ConsoleIn->EfiKeyQueue)) {
342       CopyMem (
343         &KeyData,
344         &(ConsoleIn->EfiKeyQueue.Buffer[ConsoleIn->EfiKeyQueue.Head]),
345         sizeof (EFI_KEY_DATA)
346         );
347       if (KeyData.Key.ScanCode == SCAN_NULL && KeyData.Key.UnicodeChar == CHAR_NULL) {
348         PopEfikeyBufHead (&ConsoleIn->EfiKeyQueue, &KeyData);
349         continue;
350       }
351       //
352       // if there is pending value key, signal the event.
353       //
354       gBS->SignalEvent (Event);
355       break;
356     }
357   }
358   //
359   // Leave critical section and return
360   //
361   gBS->RestoreTPL (OldTpl);
362 }
363 
364 /**
365   Event notification function for SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event
366   Signal the event if there is key available
367 
368   @param Event    event object
369   @param Context  waiting context
370 
371 **/
372 VOID
373 EFIAPI
KeyboardWaitForKeyEx(IN EFI_EVENT Event,IN VOID * Context)374 KeyboardWaitForKeyEx (
375   IN  EFI_EVENT               Event,
376   IN  VOID                    *Context
377   )
378 
379 {
380   KeyboardWaitForKey (Event, Context);
381 }
382 
383 /**
384   Reset the input device and optionaly run diagnostics
385 
386   @param This                     Protocol instance pointer.
387   @param ExtendedVerification     Driver may perform diagnostics on reset.
388 
389   @retval EFI_SUCCESS             The device was reset.
390   @retval EFI_DEVICE_ERROR        The device is not functioning properly and could
391                                   not be reset.
392 
393 **/
394 EFI_STATUS
395 EFIAPI
KeyboardEfiResetEx(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN BOOLEAN ExtendedVerification)396 KeyboardEfiResetEx (
397   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
398   IN BOOLEAN                            ExtendedVerification
399   )
400 
401 {
402   KEYBOARD_CONSOLE_IN_DEV               *ConsoleInDev;
403 
404   ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
405 
406   return ConsoleInDev->ConIn.Reset (
407                                &ConsoleInDev->ConIn,
408                                ExtendedVerification
409                                );
410 }
411 
412 /**
413     Reads the next keystroke from the input device. The WaitForKey Event can
414     be used to test for existance of a keystroke via WaitForEvent () call.
415 
416 
417     @param This         Protocol instance pointer.
418     @param KeyData      A pointer to a buffer that is filled in with the keystroke
419                         state data for the key that was pressed.
420 
421     @retval EFI_SUCCESS           The keystroke information was returned.
422     @retval EFI_NOT_READY         There was no keystroke data availiable.
423     @retval EFI_DEVICE_ERROR      The keystroke information was not returned due to
424                                   hardware errors.
425     @retval EFI_INVALID_PARAMETER KeyData is NULL.
426 
427 **/
428 EFI_STATUS
429 EFIAPI
KeyboardReadKeyStrokeEx(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,OUT EFI_KEY_DATA * KeyData)430 KeyboardReadKeyStrokeEx (
431   IN  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
432   OUT EFI_KEY_DATA                      *KeyData
433   )
434 
435 {
436   KEYBOARD_CONSOLE_IN_DEV               *ConsoleInDev;
437 
438   if (KeyData == NULL) {
439     return EFI_INVALID_PARAMETER;
440   }
441 
442   ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
443   return KeyboardReadKeyStrokeWorker (ConsoleInDev, KeyData);
444 }
445 
446 /**
447   Set certain state for the input device.
448 
449   @param This               Protocol instance pointer.
450   @param KeyToggleState     A pointer to the EFI_KEY_TOGGLE_STATE to set the
451                             state for the input device.
452 
453   @retval EFI_SUCCESS           The device state was set successfully.
454   @retval EFI_DEVICE_ERROR      The device is not functioning correctly and could
455                                 not have the setting adjusted.
456   @retval EFI_UNSUPPORTED       The device does not have the ability to set its state.
457   @retval EFI_INVALID_PARAMETER KeyToggleState is NULL.
458 
459 **/
460 EFI_STATUS
461 EFIAPI
KeyboardSetState(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN EFI_KEY_TOGGLE_STATE * KeyToggleState)462 KeyboardSetState (
463   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
464   IN EFI_KEY_TOGGLE_STATE               *KeyToggleState
465   )
466 
467 {
468   EFI_STATUS                            Status;
469   KEYBOARD_CONSOLE_IN_DEV               *ConsoleInDev;
470   EFI_TPL                               OldTpl;
471 
472   if (KeyToggleState == NULL) {
473     return EFI_INVALID_PARAMETER;
474   }
475 
476   ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
477 
478   //
479   // Enter critical section
480   //
481   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
482 
483   if (ConsoleInDev->KeyboardErr) {
484     Status = EFI_DEVICE_ERROR;
485     goto Exit;
486   }
487 
488   if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) {
489     Status = EFI_UNSUPPORTED;
490     goto Exit;
491   }
492 
493   //
494   // Update the status light
495   //
496   ConsoleInDev->ScrollLock          = FALSE;
497   ConsoleInDev->NumLock             = FALSE;
498   ConsoleInDev->CapsLock            = FALSE;
499   ConsoleInDev->IsSupportPartialKey = FALSE;
500 
501   if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) {
502     ConsoleInDev->ScrollLock = TRUE;
503   }
504   if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) {
505     ConsoleInDev->NumLock = TRUE;
506   }
507   if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) {
508     ConsoleInDev->CapsLock = TRUE;
509   }
510   if ((*KeyToggleState & EFI_KEY_STATE_EXPOSED) == EFI_KEY_STATE_EXPOSED) {
511     ConsoleInDev->IsSupportPartialKey = TRUE;
512   }
513 
514   Status = UpdateStatusLights (ConsoleInDev);
515   if (EFI_ERROR (Status)) {
516     Status = EFI_DEVICE_ERROR;
517   }
518 
519 Exit:
520   //
521   // Leave critical section and return
522   //
523   gBS->RestoreTPL (OldTpl);
524 
525   return Status;
526 
527 }
528 
529 /**
530     Register a notification function for a particular keystroke for the input device.
531 
532     @param This                       Protocol instance pointer.
533     @param KeyData                    A pointer to a buffer that is filled in with the keystroke
534                                       information data for the key that was pressed. If KeyData.Key,
535                                       KeyData.KeyState.KeyToggleState and KeyData.KeyState.KeyShiftState are 0,
536                                       then any incomplete keystroke will trigger a notification of the KeyNotificationFunction.
537     @param KeyNotificationFunction    Points to the function to be called when the key
538                                       sequence is typed specified by KeyData. This notification function
539                                       should be called at <=TPL_CALLBACK.
540     @param NotifyHandle               Points to the unique handle assigned to the registered notification.
541 
542     @retval EFI_SUCCESS               The notification function was registered successfully.
543     @retval EFI_OUT_OF_RESOURCES      Unable to allocate resources for necesssary data structures.
544     @retval EFI_INVALID_PARAMETER     KeyData or NotifyHandle or KeyNotificationFunction is NULL.
545 
546 **/
547 EFI_STATUS
548 EFIAPI
KeyboardRegisterKeyNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN EFI_KEY_DATA * KeyData,IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction,OUT VOID ** NotifyHandle)549 KeyboardRegisterKeyNotify (
550   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
551   IN EFI_KEY_DATA                       *KeyData,
552   IN EFI_KEY_NOTIFY_FUNCTION            KeyNotificationFunction,
553   OUT VOID                              **NotifyHandle
554   )
555 {
556   EFI_STATUS                            Status;
557   KEYBOARD_CONSOLE_IN_DEV               *ConsoleInDev;
558   EFI_TPL                               OldTpl;
559   LIST_ENTRY                            *Link;
560   KEYBOARD_CONSOLE_IN_EX_NOTIFY         *CurrentNotify;
561   KEYBOARD_CONSOLE_IN_EX_NOTIFY         *NewNotify;
562 
563   if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) {
564     return EFI_INVALID_PARAMETER;
565   }
566 
567   ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
568 
569   //
570   // Enter critical section
571   //
572   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
573 
574   //
575   // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered.
576   //
577   for (Link = ConsoleInDev->NotifyList.ForwardLink; Link != &ConsoleInDev->NotifyList; Link = Link->ForwardLink) {
578     CurrentNotify = CR (
579                       Link,
580                       KEYBOARD_CONSOLE_IN_EX_NOTIFY,
581                       NotifyEntry,
582                       KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
583                       );
584     if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
585       if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) {
586         *NotifyHandle = CurrentNotify;
587         Status = EFI_SUCCESS;
588         goto Exit;
589       }
590     }
591   }
592 
593   //
594   // Allocate resource to save the notification function
595   //
596   NewNotify = (KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (KEYBOARD_CONSOLE_IN_EX_NOTIFY));
597   if (NewNotify == NULL) {
598     Status = EFI_OUT_OF_RESOURCES;
599     goto Exit;
600   }
601 
602   NewNotify->Signature         = KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE;
603   NewNotify->KeyNotificationFn = KeyNotificationFunction;
604   CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
605   InsertTailList (&ConsoleInDev->NotifyList, &NewNotify->NotifyEntry);
606 
607   *NotifyHandle                = NewNotify;
608   Status                       = EFI_SUCCESS;
609 
610 Exit:
611   //
612   // Leave critical section and return
613   //
614   gBS->RestoreTPL (OldTpl);
615   return Status;
616 
617 }
618 
619 /**
620     Remove a registered notification function from a particular keystroke.
621 
622     @param This                       Protocol instance pointer.
623     @param NotificationHandle         The handle of the notification function being unregistered.
624 
625 
626     @retval EFI_SUCCESS               The notification function was unregistered successfully.
627     @retval EFI_INVALID_PARAMETER     The NotificationHandle is invalid.
628 
629 **/
630 EFI_STATUS
631 EFIAPI
KeyboardUnregisterKeyNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN VOID * NotificationHandle)632 KeyboardUnregisterKeyNotify (
633   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
634   IN VOID                               *NotificationHandle
635   )
636 {
637   EFI_STATUS                            Status;
638   KEYBOARD_CONSOLE_IN_DEV               *ConsoleInDev;
639   EFI_TPL                               OldTpl;
640   LIST_ENTRY                            *Link;
641   KEYBOARD_CONSOLE_IN_EX_NOTIFY         *CurrentNotify;
642 
643   if (NotificationHandle == NULL) {
644     return EFI_INVALID_PARAMETER;
645   }
646 
647   ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
648 
649   //
650   // Enter critical section
651   //
652   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
653 
654   for (Link = ConsoleInDev->NotifyList.ForwardLink; Link != &ConsoleInDev->NotifyList; Link = Link->ForwardLink) {
655     CurrentNotify = CR (
656                       Link,
657                       KEYBOARD_CONSOLE_IN_EX_NOTIFY,
658                       NotifyEntry,
659                       KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
660                       );
661     if (CurrentNotify == NotificationHandle) {
662       //
663       // Remove the notification function from NotifyList and free resources
664       //
665       RemoveEntryList (&CurrentNotify->NotifyEntry);
666 
667       gBS->FreePool (CurrentNotify);
668       Status = EFI_SUCCESS;
669       goto Exit;
670     }
671   }
672 
673   //
674   // Can not find the specified Notification Handle
675   //
676   Status = EFI_INVALID_PARAMETER;
677 Exit:
678   //
679   // Leave critical section and return
680   //
681   gBS->RestoreTPL (OldTpl);
682   return Status;
683 }
684 
685 /**
686   Process key notify.
687 
688   @param  Event                 Indicates the event that invoke this function.
689   @param  Context               Indicates the calling context.
690 **/
691 VOID
692 EFIAPI
KeyNotifyProcessHandler(IN EFI_EVENT Event,IN VOID * Context)693 KeyNotifyProcessHandler (
694   IN  EFI_EVENT                 Event,
695   IN  VOID                      *Context
696   )
697 {
698   EFI_STATUS                    Status;
699   KEYBOARD_CONSOLE_IN_DEV       *ConsoleIn;
700   EFI_KEY_DATA                  KeyData;
701   LIST_ENTRY                    *Link;
702   LIST_ENTRY                    *NotifyList;
703   KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
704   EFI_TPL                       OldTpl;
705 
706   ConsoleIn = (KEYBOARD_CONSOLE_IN_DEV *) Context;
707 
708   //
709   // Invoke notification functions.
710   //
711   NotifyList = &ConsoleIn->NotifyList;
712   while (TRUE) {
713     //
714     // Enter critical section
715     //
716     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
717     Status = PopEfikeyBufHead (&ConsoleIn->EfiKeyQueueForNotify, &KeyData);
718     //
719     // Leave critical section
720     //
721     gBS->RestoreTPL (OldTpl);
722     if (EFI_ERROR (Status)) {
723       break;
724     }
725     for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) {
726       CurrentNotify = CR (Link, KEYBOARD_CONSOLE_IN_EX_NOTIFY, NotifyEntry, KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE);
727       if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
728         CurrentNotify->KeyNotificationFn (&KeyData);
729       }
730     }
731   }
732 }
733 
734