1 /** @file
2   Implementation for EFI_SIMPLE_TEXT_INPUT_PROTOCOL protocol.
3 
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
6 Copyright (C) 2016 Silicon Graphics, Inc. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution.  The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11 
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "Terminal.h"
18 
19 
20 /**
21   Reads the next keystroke from the input device. The WaitForKey Event can
22   be used to test for existence of a keystroke via WaitForEvent () call.
23 
24   @param  TerminalDevice           Terminal driver private structure
25   @param  KeyData                  A pointer to a buffer that is filled in with the
26                                    keystroke state data for the key that was
27                                    pressed.
28 
29   @retval EFI_SUCCESS              The keystroke information was returned.
30   @retval EFI_NOT_READY            There was no keystroke data available.
31   @retval EFI_INVALID_PARAMETER    KeyData is NULL.
32 
33 **/
34 EFI_STATUS
ReadKeyStrokeWorker(IN TERMINAL_DEV * TerminalDevice,OUT EFI_KEY_DATA * KeyData)35 ReadKeyStrokeWorker (
36   IN  TERMINAL_DEV *TerminalDevice,
37   OUT EFI_KEY_DATA *KeyData
38   )
39 {
40   if (KeyData == NULL) {
41     return EFI_INVALID_PARAMETER;
42   }
43 
44   if (!EfiKeyFiFoRemoveOneKey (TerminalDevice, &KeyData->Key)) {
45     return EFI_NOT_READY;
46   }
47 
48   KeyData->KeyState.KeyShiftState  = 0;
49   KeyData->KeyState.KeyToggleState = 0;
50 
51 
52   return EFI_SUCCESS;
53 
54 }
55 
56 /**
57   Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.Reset().
58   This driver only perform dependent serial device reset regardless of
59   the value of ExtendeVerification
60 
61   @param  This                     Indicates the calling context.
62   @param  ExtendedVerification     Skip by this driver.
63 
64   @retval EFI_SUCCESS              The reset operation succeeds.
65   @retval EFI_DEVICE_ERROR         The dependent serial port reset fails.
66 
67 **/
68 EFI_STATUS
69 EFIAPI
TerminalConInReset(IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL * This,IN BOOLEAN ExtendedVerification)70 TerminalConInReset (
71   IN  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
72   IN  BOOLEAN                         ExtendedVerification
73   )
74 {
75   EFI_STATUS    Status;
76   TERMINAL_DEV  *TerminalDevice;
77 
78   TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (This);
79 
80   //
81   // Report progress code here
82   //
83   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
84     EFI_PROGRESS_CODE,
85     (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_PC_RESET),
86     TerminalDevice->DevicePath
87     );
88 
89   Status = TerminalDevice->SerialIo->Reset (TerminalDevice->SerialIo);
90 
91   //
92   // Make all the internal buffer empty for keys
93   //
94   TerminalDevice->RawFiFo->Head     = TerminalDevice->RawFiFo->Tail;
95   TerminalDevice->UnicodeFiFo->Head = TerminalDevice->UnicodeFiFo->Tail;
96   TerminalDevice->EfiKeyFiFo->Head  = TerminalDevice->EfiKeyFiFo->Tail;
97   TerminalDevice->EfiKeyFiFoForNotify->Head = TerminalDevice->EfiKeyFiFoForNotify->Tail;
98 
99   if (EFI_ERROR (Status)) {
100     REPORT_STATUS_CODE_WITH_DEVICE_PATH (
101       EFI_ERROR_CODE | EFI_ERROR_MINOR,
102       (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR),
103       TerminalDevice->DevicePath
104       );
105   }
106 
107   return Status;
108 }
109 
110 /**
111   Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke().
112 
113   @param  This                Indicates the calling context.
114   @param  Key                 A pointer to a buffer that is filled in with the
115                               keystroke information for the key that was sent
116                               from terminal.
117 
118   @retval EFI_SUCCESS         The keystroke information is returned successfully.
119   @retval EFI_NOT_READY       There is no keystroke data available.
120   @retval EFI_DEVICE_ERROR    The dependent serial device encounters error.
121 
122 **/
123 EFI_STATUS
124 EFIAPI
TerminalConInReadKeyStroke(IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL * This,OUT EFI_INPUT_KEY * Key)125 TerminalConInReadKeyStroke (
126   IN  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
127   OUT EFI_INPUT_KEY                   *Key
128   )
129 {
130   TERMINAL_DEV  *TerminalDevice;
131   EFI_STATUS    Status;
132   EFI_KEY_DATA  KeyData;
133 
134   //
135   //  get TERMINAL_DEV from "This" parameter.
136   //
137   TerminalDevice  = TERMINAL_CON_IN_DEV_FROM_THIS (This);
138 
139   Status = ReadKeyStrokeWorker (TerminalDevice, &KeyData);
140   if (EFI_ERROR (Status)) {
141     return Status;
142   }
143 
144   CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY));
145 
146   return EFI_SUCCESS;
147 
148 }
149 
150 /**
151   Check if the key already has been registered.
152 
153   If both RegsiteredData and InputData is NULL, then ASSERT().
154 
155   @param  RegsiteredData           A pointer to a buffer that is filled in with the
156                                    keystroke state data for the key that was
157                                    registered.
158   @param  InputData                A pointer to a buffer that is filled in with the
159                                    keystroke state data for the key that was
160                                    pressed.
161 
162   @retval TRUE                     Key be pressed matches a registered key.
163   @retval FALSE                    Match failed.
164 
165 **/
166 BOOLEAN
IsKeyRegistered(IN EFI_KEY_DATA * RegsiteredData,IN EFI_KEY_DATA * InputData)167 IsKeyRegistered (
168   IN EFI_KEY_DATA  *RegsiteredData,
169   IN EFI_KEY_DATA  *InputData
170   )
171 {
172   ASSERT (RegsiteredData != NULL && InputData != NULL);
173 
174   if ((RegsiteredData->Key.ScanCode    != InputData->Key.ScanCode) ||
175       (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) {
176     return FALSE;
177   }
178 
179   return TRUE;
180 }
181 
182 
183 
184 /**
185   Event notification function for EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event
186   Signal the event if there is key available
187 
188   @param  Event                    Indicates the event that invoke this function.
189   @param  Context                  Indicates the calling context.
190 
191 **/
192 VOID
193 EFIAPI
TerminalConInWaitForKeyEx(IN EFI_EVENT Event,IN VOID * Context)194 TerminalConInWaitForKeyEx (
195   IN  EFI_EVENT       Event,
196   IN  VOID            *Context
197   )
198 {
199   TerminalConInWaitForKey (Event, Context);
200 }
201 
202 //
203 // Simple Text Input Ex protocol functions
204 //
205 
206 /**
207   Reset the input device and optionally run diagnostics
208 
209   @param  This                     Protocol instance pointer.
210   @param  ExtendedVerification     Driver may perform diagnostics on reset.
211 
212   @retval EFI_SUCCESS              The device was reset.
213   @retval EFI_DEVICE_ERROR         The device is not functioning properly and could
214                                    not be reset.
215 
216 **/
217 EFI_STATUS
218 EFIAPI
TerminalConInResetEx(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN BOOLEAN ExtendedVerification)219 TerminalConInResetEx (
220   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
221   IN BOOLEAN                            ExtendedVerification
222   )
223 {
224   EFI_STATUS              Status;
225   TERMINAL_DEV            *TerminalDevice;
226 
227   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
228 
229   Status = TerminalDevice->SimpleInput.Reset (&TerminalDevice->SimpleInput, ExtendedVerification);
230   if (EFI_ERROR (Status)) {
231     return EFI_DEVICE_ERROR;
232   }
233 
234   return EFI_SUCCESS;
235 
236 }
237 
238 
239 /**
240   Reads the next keystroke from the input device. The WaitForKey Event can
241   be used to test for existence of a keystroke via WaitForEvent () call.
242 
243   @param  This                     Protocol instance pointer.
244   @param  KeyData                  A pointer to a buffer that is filled in with the
245                                    keystroke state data for the key that was
246                                    pressed.
247 
248   @retval EFI_SUCCESS              The keystroke information was returned.
249   @retval EFI_NOT_READY            There was no keystroke data available.
250   @retval EFI_DEVICE_ERROR         The keystroke information was not returned due
251                                    to hardware errors.
252   @retval EFI_INVALID_PARAMETER    KeyData is NULL.
253 
254 **/
255 EFI_STATUS
256 EFIAPI
TerminalConInReadKeyStrokeEx(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,OUT EFI_KEY_DATA * KeyData)257 TerminalConInReadKeyStrokeEx (
258   IN  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
259   OUT EFI_KEY_DATA                      *KeyData
260   )
261 {
262   TERMINAL_DEV                    *TerminalDevice;
263 
264   if (KeyData == NULL) {
265     return EFI_INVALID_PARAMETER;
266   }
267 
268   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
269 
270   return ReadKeyStrokeWorker (TerminalDevice, KeyData);
271 
272 }
273 
274 
275 /**
276   Set certain state for the input device.
277 
278   @param  This                     Protocol instance pointer.
279   @param  KeyToggleState           A pointer to the EFI_KEY_TOGGLE_STATE to set the
280                                    state for the input device.
281 
282   @retval EFI_SUCCESS              The device state was set successfully.
283   @retval EFI_DEVICE_ERROR         The device is not functioning correctly and
284                                    could not have the setting adjusted.
285   @retval EFI_UNSUPPORTED          The device does not have the ability to set its
286                                    state.
287   @retval EFI_INVALID_PARAMETER    KeyToggleState is NULL.
288 
289 **/
290 EFI_STATUS
291 EFIAPI
TerminalConInSetState(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN EFI_KEY_TOGGLE_STATE * KeyToggleState)292 TerminalConInSetState (
293   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
294   IN EFI_KEY_TOGGLE_STATE               *KeyToggleState
295   )
296 {
297   if (KeyToggleState == NULL) {
298     return EFI_INVALID_PARAMETER;
299   }
300 
301   if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) {
302     return EFI_UNSUPPORTED;
303   }
304 
305   return EFI_SUCCESS;
306 }
307 
308 
309 /**
310   Register a notification function for a particular keystroke for the input device.
311 
312   @param  This                     Protocol instance pointer.
313   @param  KeyData                  A pointer to a buffer that is filled in with the
314                                    keystroke information data for the key that was
315                                    pressed.
316   @param  KeyNotificationFunction  Points to the function to be called when the key
317                                    sequence is typed specified by KeyData.
318   @param  NotifyHandle             Points to the unique handle assigned to the
319                                    registered notification.
320 
321   @retval EFI_SUCCESS              The notification function was registered
322                                    successfully.
323   @retval EFI_OUT_OF_RESOURCES     Unable to allocate resources for necessary data
324                                    structures.
325   @retval EFI_INVALID_PARAMETER    KeyData or NotifyHandle is NULL.
326 
327 **/
328 EFI_STATUS
329 EFIAPI
TerminalConInRegisterKeyNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN EFI_KEY_DATA * KeyData,IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction,OUT VOID ** NotifyHandle)330 TerminalConInRegisterKeyNotify (
331   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
332   IN EFI_KEY_DATA                       *KeyData,
333   IN EFI_KEY_NOTIFY_FUNCTION            KeyNotificationFunction,
334   OUT VOID                              **NotifyHandle
335   )
336 {
337   TERMINAL_DEV                    *TerminalDevice;
338   TERMINAL_CONSOLE_IN_EX_NOTIFY   *NewNotify;
339   LIST_ENTRY                      *Link;
340   LIST_ENTRY                      *NotifyList;
341   TERMINAL_CONSOLE_IN_EX_NOTIFY   *CurrentNotify;
342 
343   if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) {
344     return EFI_INVALID_PARAMETER;
345   }
346 
347   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
348 
349   //
350   // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered.
351   //
352   NotifyList = &TerminalDevice->NotifyList;
353   for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) {
354     CurrentNotify = CR (
355                       Link,
356                       TERMINAL_CONSOLE_IN_EX_NOTIFY,
357                       NotifyEntry,
358                       TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
359                       );
360     if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
361       if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) {
362         *NotifyHandle = CurrentNotify;
363         return EFI_SUCCESS;
364       }
365     }
366   }
367 
368   //
369   // Allocate resource to save the notification function
370   //
371   NewNotify = (TERMINAL_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (TERMINAL_CONSOLE_IN_EX_NOTIFY));
372   if (NewNotify == NULL) {
373     return EFI_OUT_OF_RESOURCES;
374   }
375 
376   NewNotify->Signature         = TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE;
377   NewNotify->KeyNotificationFn = KeyNotificationFunction;
378   CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
379   InsertTailList (&TerminalDevice->NotifyList, &NewNotify->NotifyEntry);
380 
381   *NotifyHandle                = NewNotify;
382 
383   return EFI_SUCCESS;
384 }
385 
386 
387 /**
388   Remove a registered notification function from a particular keystroke.
389 
390   @param  This                     Protocol instance pointer.
391   @param  NotificationHandle       The handle of the notification function being
392                                    unregistered.
393 
394   @retval EFI_SUCCESS              The notification function was unregistered
395                                    successfully.
396   @retval EFI_INVALID_PARAMETER    The NotificationHandle is invalid.
397 
398 **/
399 EFI_STATUS
400 EFIAPI
TerminalConInUnregisterKeyNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN VOID * NotificationHandle)401 TerminalConInUnregisterKeyNotify (
402   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
403   IN VOID                               *NotificationHandle
404   )
405 {
406   TERMINAL_DEV                    *TerminalDevice;
407   LIST_ENTRY                      *Link;
408   TERMINAL_CONSOLE_IN_EX_NOTIFY   *CurrentNotify;
409   LIST_ENTRY                      *NotifyList;
410 
411   if (NotificationHandle == NULL) {
412     return EFI_INVALID_PARAMETER;
413   }
414 
415   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
416 
417   NotifyList = &TerminalDevice->NotifyList;
418   for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) {
419     CurrentNotify = CR (
420                       Link,
421                       TERMINAL_CONSOLE_IN_EX_NOTIFY,
422                       NotifyEntry,
423                       TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
424                       );
425     if (CurrentNotify == NotificationHandle) {
426       //
427       // Remove the notification function from NotifyList and free resources
428       //
429       RemoveEntryList (&CurrentNotify->NotifyEntry);
430 
431       gBS->FreePool (CurrentNotify);
432       return EFI_SUCCESS;
433     }
434   }
435 
436   //
437   // Can not find the matching entry in database.
438   //
439   return EFI_INVALID_PARAMETER;
440 }
441 
442 /**
443   Translate raw data into Unicode (according to different encode), and
444   translate Unicode into key information. (according to different standard).
445 
446   @param  TerminalDevice       Terminal driver private structure.
447 
448 **/
449 VOID
TranslateRawDataToEfiKey(IN TERMINAL_DEV * TerminalDevice)450 TranslateRawDataToEfiKey (
451   IN  TERMINAL_DEV      *TerminalDevice
452   )
453 {
454   switch (TerminalDevice->TerminalType) {
455 
456   case PCANSITYPE:
457   case VT100TYPE:
458   case VT100PLUSTYPE:
459   case TTYTERMTYPE:
460     AnsiRawDataToUnicode (TerminalDevice);
461     UnicodeToEfiKey (TerminalDevice);
462     break;
463 
464   case VTUTF8TYPE:
465     //
466     // Process all the raw data in the RawFIFO,
467     // put the processed key into UnicodeFIFO.
468     //
469     VTUTF8RawDataToUnicode (TerminalDevice);
470 
471     //
472     // Translate all the Unicode data in the UnicodeFIFO to Efi key,
473     // then put into EfiKeyFIFO.
474     //
475     UnicodeToEfiKey (TerminalDevice);
476 
477     break;
478   }
479 }
480 
481 /**
482   Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event
483   Signal the event if there is key available
484 
485   @param  Event                    Indicates the event that invoke this function.
486   @param  Context                  Indicates the calling context.
487 
488 **/
489 VOID
490 EFIAPI
TerminalConInWaitForKey(IN EFI_EVENT Event,IN VOID * Context)491 TerminalConInWaitForKey (
492   IN  EFI_EVENT       Event,
493   IN  VOID            *Context
494   )
495 {
496   //
497   // Someone is waiting on the keystroke event, if there's
498   // a key pending, signal the event
499   //
500   if (!IsEfiKeyFiFoEmpty ((TERMINAL_DEV *) Context)) {
501 
502     gBS->SignalEvent (Event);
503   }
504 }
505 
506 /**
507   Timer handler to poll the key from serial.
508 
509   @param  Event                    Indicates the event that invoke this function.
510   @param  Context                  Indicates the calling context.
511 **/
512 VOID
513 EFIAPI
TerminalConInTimerHandler(IN EFI_EVENT Event,IN VOID * Context)514 TerminalConInTimerHandler (
515   IN EFI_EVENT            Event,
516   IN VOID                 *Context
517   )
518 {
519   EFI_STATUS              Status;
520   TERMINAL_DEV            *TerminalDevice;
521   UINT32                  Control;
522   UINT8                   Input;
523   EFI_SERIAL_IO_MODE      *Mode;
524   EFI_SERIAL_IO_PROTOCOL  *SerialIo;
525   UINTN                   SerialInTimeOut;
526 
527   TerminalDevice  = (TERMINAL_DEV *) Context;
528 
529   SerialIo        = TerminalDevice->SerialIo;
530   if (SerialIo == NULL) {
531     return ;
532   }
533   //
534   //  if current timeout value for serial device is not identical with
535   //  the value saved in TERMINAL_DEV structure, then recalculate the
536   //  timeout value again and set serial attribute according to this value.
537   //
538   Mode = SerialIo->Mode;
539   if (Mode->Timeout != TerminalDevice->SerialInTimeOut) {
540 
541     SerialInTimeOut = 0;
542     if (Mode->BaudRate != 0) {
543       //
544       // According to BAUD rate to calculate the timeout value.
545       //
546       SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate;
547     }
548 
549     Status = SerialIo->SetAttributes (
550                         SerialIo,
551                         Mode->BaudRate,
552                         Mode->ReceiveFifoDepth,
553                         (UINT32) SerialInTimeOut,
554                         (EFI_PARITY_TYPE) (Mode->Parity),
555                         (UINT8) Mode->DataBits,
556                         (EFI_STOP_BITS_TYPE) (Mode->StopBits)
557                         );
558 
559     if (EFI_ERROR (Status)) {
560       TerminalDevice->SerialInTimeOut = 0;
561     } else {
562       TerminalDevice->SerialInTimeOut = SerialInTimeOut;
563     }
564   }
565   //
566   // Check whether serial buffer is empty.
567   // Skip the key transfer loop only if the SerialIo protocol instance
568   // successfully reports EFI_SERIAL_INPUT_BUFFER_EMPTY.
569   //
570   Status = SerialIo->GetControl (SerialIo, &Control);
571   if (EFI_ERROR (Status) || ((Control & EFI_SERIAL_INPUT_BUFFER_EMPTY) == 0)) {
572     //
573     // Fetch all the keys in the serial buffer,
574     // and insert the byte stream into RawFIFO.
575     //
576     while (!IsRawFiFoFull (TerminalDevice)) {
577 
578       Status = GetOneKeyFromSerial (TerminalDevice->SerialIo, &Input);
579 
580       if (EFI_ERROR (Status)) {
581         if (Status == EFI_DEVICE_ERROR) {
582           REPORT_STATUS_CODE_WITH_DEVICE_PATH (
583             EFI_ERROR_CODE | EFI_ERROR_MINOR,
584             (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_INPUT_ERROR),
585             TerminalDevice->DevicePath
586             );
587         }
588         break;
589       }
590 
591       RawFiFoInsertOneKey (TerminalDevice, Input);
592     }
593   }
594 
595   //
596   // Translate all the raw data in RawFIFO into EFI Key,
597   // according to different terminal type supported.
598   //
599   TranslateRawDataToEfiKey (TerminalDevice);
600 }
601 
602 /**
603   Process key notify.
604 
605   @param  Event                 Indicates the event that invoke this function.
606   @param  Context               Indicates the calling context.
607 **/
608 VOID
609 EFIAPI
KeyNotifyProcessHandler(IN EFI_EVENT Event,IN VOID * Context)610 KeyNotifyProcessHandler (
611   IN  EFI_EVENT                 Event,
612   IN  VOID                      *Context
613   )
614 {
615   BOOLEAN                       HasKey;
616   TERMINAL_DEV                  *TerminalDevice;
617   EFI_INPUT_KEY                 Key;
618   EFI_KEY_DATA                  KeyData;
619   LIST_ENTRY                    *Link;
620   LIST_ENTRY                    *NotifyList;
621   TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
622   EFI_TPL                       OldTpl;
623 
624   TerminalDevice = (TERMINAL_DEV *) Context;
625 
626   //
627   // Invoke notification functions.
628   //
629   NotifyList = &TerminalDevice->NotifyList;
630   while (TRUE) {
631     //
632     // Enter critical section
633     //
634     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
635     HasKey = EfiKeyFiFoForNotifyRemoveOneKey (TerminalDevice->EfiKeyFiFoForNotify, &Key);
636     CopyMem (&KeyData.Key, &Key, sizeof (EFI_INPUT_KEY));
637     KeyData.KeyState.KeyShiftState  = 0;
638     KeyData.KeyState.KeyToggleState = 0;
639     //
640     // Leave critical section
641     //
642     gBS->RestoreTPL (OldTpl);
643     if (!HasKey) {
644       break;
645     }
646     for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) {
647       CurrentNotify = CR (Link, TERMINAL_CONSOLE_IN_EX_NOTIFY, NotifyEntry, TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE);
648       if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
649         CurrentNotify->KeyNotificationFn (&KeyData);
650       }
651     }
652   }
653 }
654 
655 /**
656   Get one key out of serial buffer.
657 
658   @param  SerialIo           Serial I/O protocol attached to the serial device.
659   @param  Output             The fetched key.
660 
661   @retval EFI_NOT_READY      If serial buffer is empty.
662   @retval EFI_DEVICE_ERROR   If reading serial buffer encounter error.
663   @retval EFI_SUCCESS        If reading serial buffer successfully, put
664                              the fetched key to the parameter output.
665 
666 **/
667 EFI_STATUS
GetOneKeyFromSerial(EFI_SERIAL_IO_PROTOCOL * SerialIo,UINT8 * Output)668 GetOneKeyFromSerial (
669   EFI_SERIAL_IO_PROTOCOL  *SerialIo,
670   UINT8                   *Output
671   )
672 {
673   EFI_STATUS  Status;
674   UINTN       Size;
675 
676   Size    = 1;
677   *Output = 0;
678 
679   //
680   // Read one key from serial I/O device.
681   //
682   Status  = SerialIo->Read (SerialIo, &Size, Output);
683 
684   if (EFI_ERROR (Status)) {
685 
686     if (Status == EFI_TIMEOUT) {
687       return EFI_NOT_READY;
688     }
689 
690     return EFI_DEVICE_ERROR;
691 
692   }
693 
694   if (*Output == 0) {
695     return EFI_NOT_READY;
696   }
697 
698   return EFI_SUCCESS;
699 }
700 
701 /**
702   Insert one byte raw data into the Raw Data FIFO.
703 
704   @param  TerminalDevice       Terminal driver private structure.
705   @param  Input                The key will be input.
706 
707   @retval TRUE                 If insert successfully.
708   @retval FALSE                If Raw Data buffer is full before key insertion,
709                                and the key is lost.
710 
711 **/
712 BOOLEAN
RawFiFoInsertOneKey(TERMINAL_DEV * TerminalDevice,UINT8 Input)713 RawFiFoInsertOneKey (
714   TERMINAL_DEV      *TerminalDevice,
715   UINT8             Input
716   )
717 {
718   UINT8 Tail;
719 
720   Tail = TerminalDevice->RawFiFo->Tail;
721 
722   if (IsRawFiFoFull (TerminalDevice)) {
723     //
724     // Raw FIFO is full
725     //
726     return FALSE;
727   }
728 
729   TerminalDevice->RawFiFo->Data[Tail]  = Input;
730 
731   TerminalDevice->RawFiFo->Tail        = (UINT8) ((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1));
732 
733   return TRUE;
734 }
735 
736 /**
737   Remove one pre-fetched key out of the Raw Data FIFO.
738 
739   @param  TerminalDevice       Terminal driver private structure.
740   @param  Output               The key will be removed.
741 
742   @retval TRUE                 If insert successfully.
743   @retval FALSE                If Raw Data FIFO buffer is empty before remove operation.
744 
745 **/
746 BOOLEAN
RawFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,UINT8 * Output)747 RawFiFoRemoveOneKey (
748   TERMINAL_DEV  *TerminalDevice,
749   UINT8         *Output
750   )
751 {
752   UINT8 Head;
753 
754   Head = TerminalDevice->RawFiFo->Head;
755 
756   if (IsRawFiFoEmpty (TerminalDevice)) {
757     //
758     //  FIFO is empty
759     //
760     *Output = 0;
761     return FALSE;
762   }
763 
764   *Output                       = TerminalDevice->RawFiFo->Data[Head];
765 
766   TerminalDevice->RawFiFo->Head  = (UINT8) ((Head + 1) % (RAW_FIFO_MAX_NUMBER + 1));
767 
768   return TRUE;
769 }
770 
771 /**
772   Clarify whether Raw Data FIFO buffer is empty.
773 
774   @param  TerminalDevice       Terminal driver private structure
775 
776   @retval TRUE                 If Raw Data FIFO buffer is empty.
777   @retval FALSE                If Raw Data FIFO buffer is not empty.
778 
779 **/
780 BOOLEAN
IsRawFiFoEmpty(TERMINAL_DEV * TerminalDevice)781 IsRawFiFoEmpty (
782   TERMINAL_DEV  *TerminalDevice
783   )
784 {
785   if (TerminalDevice->RawFiFo->Head == TerminalDevice->RawFiFo->Tail) {
786     return TRUE;
787   } else {
788     return FALSE;
789   }
790 }
791 
792 /**
793   Clarify whether Raw Data FIFO buffer is full.
794 
795   @param  TerminalDevice       Terminal driver private structure
796 
797   @retval TRUE                 If Raw Data FIFO buffer is full.
798   @retval FALSE                If Raw Data FIFO buffer is not full.
799 
800 **/
801 BOOLEAN
IsRawFiFoFull(TERMINAL_DEV * TerminalDevice)802 IsRawFiFoFull (
803   TERMINAL_DEV  *TerminalDevice
804   )
805 {
806   UINT8 Tail;
807   UINT8 Head;
808 
809   Tail  = TerminalDevice->RawFiFo->Tail;
810   Head  = TerminalDevice->RawFiFo->Head;
811 
812   if (((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1)) == Head) {
813 
814     return TRUE;
815   }
816 
817   return FALSE;
818 }
819 
820 /**
821   Insert one pre-fetched key into the FIFO buffer.
822 
823   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
824   @param  Input                 The key will be input.
825 
826   @retval TRUE                  If insert successfully.
827   @retval FALSE                 If FIFO buffer is full before key insertion,
828                                 and the key is lost.
829 
830 **/
831 BOOLEAN
EfiKeyFiFoForNotifyInsertOneKey(EFI_KEY_FIFO * EfiKeyFiFo,EFI_INPUT_KEY * Input)832 EfiKeyFiFoForNotifyInsertOneKey (
833   EFI_KEY_FIFO                  *EfiKeyFiFo,
834   EFI_INPUT_KEY                 *Input
835   )
836 {
837   UINT8                         Tail;
838 
839   Tail = EfiKeyFiFo->Tail;
840 
841   if (IsEfiKeyFiFoForNotifyFull (EfiKeyFiFo)) {
842     //
843     // FIFO is full
844     //
845     return FALSE;
846   }
847 
848   CopyMem (&EfiKeyFiFo->Data[Tail], Input, sizeof (EFI_INPUT_KEY));
849 
850   EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
851 
852   return TRUE;
853 }
854 
855 /**
856   Remove one pre-fetched key out of the FIFO buffer.
857 
858   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
859   @param  Output                The key will be removed.
860 
861   @retval TRUE                  If remove successfully.
862   @retval FALSE                 If FIFO buffer is empty before remove operation.
863 
864 **/
865 BOOLEAN
EfiKeyFiFoForNotifyRemoveOneKey(EFI_KEY_FIFO * EfiKeyFiFo,EFI_INPUT_KEY * Output)866 EfiKeyFiFoForNotifyRemoveOneKey (
867   EFI_KEY_FIFO                  *EfiKeyFiFo,
868   EFI_INPUT_KEY                 *Output
869   )
870 {
871   UINT8                         Head;
872 
873   Head = EfiKeyFiFo->Head;
874   ASSERT (Head < FIFO_MAX_NUMBER + 1);
875 
876   if (IsEfiKeyFiFoForNotifyEmpty (EfiKeyFiFo)) {
877     //
878     // FIFO is empty
879     //
880     Output->ScanCode    = SCAN_NULL;
881     Output->UnicodeChar = 0;
882     return FALSE;
883   }
884 
885   CopyMem (Output, &EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY));
886 
887   EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
888 
889   return TRUE;
890 }
891 
892 /**
893   Clarify whether FIFO buffer is empty.
894 
895   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
896 
897   @retval TRUE                  If FIFO buffer is empty.
898   @retval FALSE                 If FIFO buffer is not empty.
899 
900 **/
901 BOOLEAN
IsEfiKeyFiFoForNotifyEmpty(EFI_KEY_FIFO * EfiKeyFiFo)902 IsEfiKeyFiFoForNotifyEmpty (
903   EFI_KEY_FIFO                  *EfiKeyFiFo
904   )
905 {
906   if (EfiKeyFiFo->Head == EfiKeyFiFo->Tail) {
907     return TRUE;
908   } else {
909     return FALSE;
910   }
911 }
912 
913 /**
914   Clarify whether FIFO buffer is full.
915 
916   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
917 
918   @retval TRUE                  If FIFO buffer is full.
919   @retval FALSE                 If FIFO buffer is not full.
920 
921 **/
922 BOOLEAN
IsEfiKeyFiFoForNotifyFull(EFI_KEY_FIFO * EfiKeyFiFo)923 IsEfiKeyFiFoForNotifyFull (
924   EFI_KEY_FIFO                  *EfiKeyFiFo
925   )
926 {
927   UINT8                         Tail;
928   UINT8                         Head;
929 
930   Tail = EfiKeyFiFo->Tail;
931   Head = EfiKeyFiFo->Head;
932 
933   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
934     return TRUE;
935   }
936 
937   return FALSE;
938 }
939 
940 /**
941   Insert one pre-fetched key into the FIFO buffer.
942 
943   @param  TerminalDevice       Terminal driver private structure.
944   @param  Key                  The key will be input.
945 
946   @retval TRUE                 If insert successfully.
947   @retval FALSE                If FIFO buffer is full before key insertion,
948                                and the key is lost.
949 
950 **/
951 BOOLEAN
EfiKeyFiFoInsertOneKey(TERMINAL_DEV * TerminalDevice,EFI_INPUT_KEY * Key)952 EfiKeyFiFoInsertOneKey (
953   TERMINAL_DEV                    *TerminalDevice,
954   EFI_INPUT_KEY                   *Key
955   )
956 {
957   UINT8                           Tail;
958   LIST_ENTRY                      *Link;
959   LIST_ENTRY                      *NotifyList;
960   TERMINAL_CONSOLE_IN_EX_NOTIFY   *CurrentNotify;
961   EFI_KEY_DATA                    KeyData;
962 
963   Tail = TerminalDevice->EfiKeyFiFo->Tail;
964 
965   CopyMem (&KeyData.Key, Key, sizeof (EFI_INPUT_KEY));
966   KeyData.KeyState.KeyShiftState  = 0;
967   KeyData.KeyState.KeyToggleState = 0;
968 
969   //
970   // Signal KeyNotify process event if this key pressed matches any key registered.
971   //
972   NotifyList = &TerminalDevice->NotifyList;
973   for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) {
974     CurrentNotify = CR (
975                       Link,
976                       TERMINAL_CONSOLE_IN_EX_NOTIFY,
977                       NotifyEntry,
978                       TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
979                       );
980     if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
981       //
982       // The key notification function needs to run at TPL_CALLBACK
983       // while current TPL is TPL_NOTIFY. It will be invoked in
984       // KeyNotifyProcessHandler() which runs at TPL_CALLBACK.
985       //
986       EfiKeyFiFoForNotifyInsertOneKey (TerminalDevice->EfiKeyFiFoForNotify, Key);
987       gBS->SignalEvent (TerminalDevice->KeyNotifyProcessEvent);
988     }
989   }
990   if (IsEfiKeyFiFoFull (TerminalDevice)) {
991     //
992     // Efi Key FIFO is full
993     //
994     return FALSE;
995   }
996 
997   CopyMem (&TerminalDevice->EfiKeyFiFo->Data[Tail], Key, sizeof (EFI_INPUT_KEY));
998 
999   TerminalDevice->EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
1000 
1001   return TRUE;
1002 }
1003 
1004 /**
1005   Remove one pre-fetched key out of the FIFO buffer.
1006 
1007   @param  TerminalDevice       Terminal driver private structure.
1008   @param  Output               The key will be removed.
1009 
1010   @retval TRUE                 If insert successfully.
1011   @retval FALSE                If FIFO buffer is empty before remove operation.
1012 
1013 **/
1014 BOOLEAN
EfiKeyFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,EFI_INPUT_KEY * Output)1015 EfiKeyFiFoRemoveOneKey (
1016   TERMINAL_DEV  *TerminalDevice,
1017   EFI_INPUT_KEY *Output
1018   )
1019 {
1020   UINT8 Head;
1021 
1022   Head = TerminalDevice->EfiKeyFiFo->Head;
1023   ASSERT (Head < FIFO_MAX_NUMBER + 1);
1024 
1025   if (IsEfiKeyFiFoEmpty (TerminalDevice)) {
1026     //
1027     //  FIFO is empty
1028     //
1029     Output->ScanCode    = SCAN_NULL;
1030     Output->UnicodeChar = 0;
1031     return FALSE;
1032   }
1033 
1034   CopyMem (Output, &TerminalDevice->EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY));
1035 
1036   TerminalDevice->EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
1037 
1038   return TRUE;
1039 }
1040 
1041 /**
1042   Clarify whether FIFO buffer is empty.
1043 
1044   @param  TerminalDevice       Terminal driver private structure
1045 
1046   @retval TRUE                 If FIFO buffer is empty.
1047   @retval FALSE                If FIFO buffer is not empty.
1048 
1049 **/
1050 BOOLEAN
IsEfiKeyFiFoEmpty(TERMINAL_DEV * TerminalDevice)1051 IsEfiKeyFiFoEmpty (
1052   TERMINAL_DEV  *TerminalDevice
1053   )
1054 {
1055   if (TerminalDevice->EfiKeyFiFo->Head == TerminalDevice->EfiKeyFiFo->Tail) {
1056     return TRUE;
1057   } else {
1058     return FALSE;
1059   }
1060 }
1061 
1062 /**
1063   Clarify whether FIFO buffer is full.
1064 
1065   @param  TerminalDevice       Terminal driver private structure
1066 
1067   @retval TRUE                 If FIFO buffer is full.
1068   @retval FALSE                If FIFO buffer is not full.
1069 
1070 **/
1071 BOOLEAN
IsEfiKeyFiFoFull(TERMINAL_DEV * TerminalDevice)1072 IsEfiKeyFiFoFull (
1073   TERMINAL_DEV  *TerminalDevice
1074   )
1075 {
1076   UINT8 Tail;
1077   UINT8 Head;
1078 
1079   Tail  = TerminalDevice->EfiKeyFiFo->Tail;
1080   Head  = TerminalDevice->EfiKeyFiFo->Head;
1081 
1082   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
1083 
1084     return TRUE;
1085   }
1086 
1087   return FALSE;
1088 }
1089 
1090 /**
1091   Insert one pre-fetched key into the Unicode FIFO buffer.
1092 
1093   @param  TerminalDevice       Terminal driver private structure.
1094   @param  Input                The key will be input.
1095 
1096   @retval TRUE                 If insert successfully.
1097   @retval FALSE                If Unicode FIFO buffer is full before key insertion,
1098                                and the key is lost.
1099 
1100 **/
1101 BOOLEAN
UnicodeFiFoInsertOneKey(TERMINAL_DEV * TerminalDevice,UINT16 Input)1102 UnicodeFiFoInsertOneKey (
1103   TERMINAL_DEV      *TerminalDevice,
1104   UINT16            Input
1105   )
1106 {
1107   UINT8 Tail;
1108 
1109   Tail = TerminalDevice->UnicodeFiFo->Tail;
1110   ASSERT (Tail < FIFO_MAX_NUMBER + 1);
1111 
1112 
1113   if (IsUnicodeFiFoFull (TerminalDevice)) {
1114     //
1115     // Unicode FIFO is full
1116     //
1117     return FALSE;
1118   }
1119 
1120   TerminalDevice->UnicodeFiFo->Data[Tail]  = Input;
1121 
1122   TerminalDevice->UnicodeFiFo->Tail        = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
1123 
1124   return TRUE;
1125 }
1126 
1127 /**
1128   Remove one pre-fetched key out of the Unicode FIFO buffer.
1129   The caller should guarantee that Unicode FIFO buffer is not empty
1130   by IsUnicodeFiFoEmpty ().
1131 
1132   @param  TerminalDevice       Terminal driver private structure.
1133   @param  Output               The key will be removed.
1134 
1135 **/
1136 VOID
UnicodeFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,UINT16 * Output)1137 UnicodeFiFoRemoveOneKey (
1138   TERMINAL_DEV  *TerminalDevice,
1139   UINT16        *Output
1140   )
1141 {
1142   UINT8 Head;
1143 
1144   Head = TerminalDevice->UnicodeFiFo->Head;
1145   ASSERT (Head < FIFO_MAX_NUMBER + 1);
1146 
1147   *Output = TerminalDevice->UnicodeFiFo->Data[Head];
1148 
1149   TerminalDevice->UnicodeFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
1150 }
1151 
1152 /**
1153   Clarify whether Unicode FIFO buffer is empty.
1154 
1155   @param  TerminalDevice       Terminal driver private structure
1156 
1157   @retval TRUE                 If Unicode FIFO buffer is empty.
1158   @retval FALSE                If Unicode FIFO buffer is not empty.
1159 
1160 **/
1161 BOOLEAN
IsUnicodeFiFoEmpty(TERMINAL_DEV * TerminalDevice)1162 IsUnicodeFiFoEmpty (
1163   TERMINAL_DEV  *TerminalDevice
1164   )
1165 {
1166   if (TerminalDevice->UnicodeFiFo->Head == TerminalDevice->UnicodeFiFo->Tail) {
1167     return TRUE;
1168   } else {
1169     return FALSE;
1170   }
1171 }
1172 
1173 /**
1174   Clarify whether Unicode FIFO buffer is full.
1175 
1176   @param  TerminalDevice       Terminal driver private structure
1177 
1178   @retval TRUE                 If Unicode FIFO buffer is full.
1179   @retval FALSE                If Unicode FIFO buffer is not full.
1180 
1181 **/
1182 BOOLEAN
IsUnicodeFiFoFull(TERMINAL_DEV * TerminalDevice)1183 IsUnicodeFiFoFull (
1184   TERMINAL_DEV  *TerminalDevice
1185   )
1186 {
1187   UINT8 Tail;
1188   UINT8 Head;
1189 
1190   Tail  = TerminalDevice->UnicodeFiFo->Tail;
1191   Head  = TerminalDevice->UnicodeFiFo->Head;
1192 
1193   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
1194 
1195     return TRUE;
1196   }
1197 
1198   return FALSE;
1199 }
1200 
1201 /**
1202   Count Unicode FIFO buffer.
1203 
1204   @param  TerminalDevice       Terminal driver private structure
1205 
1206   @return The count in bytes of Unicode FIFO.
1207 
1208 **/
1209 UINT8
UnicodeFiFoGetKeyCount(TERMINAL_DEV * TerminalDevice)1210 UnicodeFiFoGetKeyCount (
1211   TERMINAL_DEV    *TerminalDevice
1212   )
1213 {
1214   UINT8 Tail;
1215   UINT8 Head;
1216 
1217   Tail  = TerminalDevice->UnicodeFiFo->Tail;
1218   Head  = TerminalDevice->UnicodeFiFo->Head;
1219 
1220   if (Tail >= Head) {
1221     return (UINT8) (Tail - Head);
1222   } else {
1223     return (UINT8) (Tail + FIFO_MAX_NUMBER + 1 - Head);
1224   }
1225 }
1226 
1227 /**
1228   Update the Unicode characters from a terminal input device into EFI Keys FIFO.
1229 
1230   @param TerminalDevice   The terminal device to use to translate raw input into EFI Keys
1231 
1232 **/
1233 VOID
UnicodeToEfiKeyFlushState(IN TERMINAL_DEV * TerminalDevice)1234 UnicodeToEfiKeyFlushState (
1235   IN  TERMINAL_DEV    *TerminalDevice
1236   )
1237 {
1238   EFI_INPUT_KEY Key;
1239   UINT32        InputState;
1240 
1241   InputState = TerminalDevice->InputState;
1242 
1243   if (IsEfiKeyFiFoFull (TerminalDevice)) {
1244     return;
1245   }
1246 
1247   if ((InputState & INPUT_STATE_ESC) != 0) {
1248     Key.ScanCode    = SCAN_ESC;
1249     Key.UnicodeChar = 0;
1250     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1251   }
1252 
1253   if ((InputState & INPUT_STATE_CSI) != 0) {
1254     Key.ScanCode    = SCAN_NULL;
1255     Key.UnicodeChar = CSI;
1256     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1257   }
1258 
1259   if ((InputState & INPUT_STATE_LEFTOPENBRACKET) != 0) {
1260     Key.ScanCode    = SCAN_NULL;
1261     Key.UnicodeChar = LEFTOPENBRACKET;
1262     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1263   }
1264 
1265   if ((InputState & INPUT_STATE_O) != 0) {
1266     Key.ScanCode    = SCAN_NULL;
1267     Key.UnicodeChar = 'O';
1268     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1269   }
1270 
1271   if ((InputState & INPUT_STATE_2) != 0) {
1272     Key.ScanCode    = SCAN_NULL;
1273     Key.UnicodeChar = '2';
1274     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1275   }
1276 
1277   //
1278   // Cancel the timer.
1279   //
1280   gBS->SetTimer (
1281         TerminalDevice->TwoSecondTimeOut,
1282         TimerCancel,
1283         0
1284         );
1285 
1286   TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1287 }
1288 
1289 
1290 /**
1291   Converts a stream of Unicode characters from a terminal input device into EFI Keys that
1292   can be read through the Simple Input Protocol.
1293 
1294   The table below shows the keyboard input mappings that this function supports.
1295   If the ESC sequence listed in one of the columns is presented, then it is translated
1296   into the corresponding EFI Scan Code.  If a matching sequence is not found, then the raw
1297   key strokes are converted into EFI Keys.
1298 
1299   2 seconds are allowed for an ESC sequence to be completed.  If the ESC sequence is not
1300   completed in 2 seconds, then the raw key strokes of the partial ESC sequence are
1301   converted into EFI Keys.
1302   There is one special input sequence that will force the system to reset.
1303   This is ESC R ESC r ESC R.
1304 
1305   Note: current implementation support terminal types include: PC ANSI, VT100+/VTUTF8, VT100.
1306         The table below is not same with UEFI Spec 2.3 Appendix B Table 201(not support ANSI X3.64 /
1307         DEC VT200-500 and extra support PC ANSI, VT100)since UEFI Table 201 is just an example.
1308 
1309   Symbols used in table below
1310   ===========================
1311     ESC = 0x1B
1312     CSI = 0x9B
1313     DEL = 0x7f
1314     ^   = CTRL
1315 
1316   +=========+======+===========+==========+==========+
1317   |         | EFI  | UEFI 2.0  |          |          |
1318   |         | Scan |           |  VT100+  |          |
1319   |   KEY   | Code |  PC ANSI  |  VTUTF8  |   VT100  |
1320   +=========+======+===========+==========+==========+
1321   | NULL    | 0x00 |           |          |          |
1322   | UP      | 0x01 | ESC [ A   | ESC [ A  | ESC [ A  |
1323   | DOWN    | 0x02 | ESC [ B   | ESC [ B  | ESC [ B  |
1324   | RIGHT   | 0x03 | ESC [ C   | ESC [ C  | ESC [ C  |
1325   | LEFT    | 0x04 | ESC [ D   | ESC [ D  | ESC [ D  |
1326   | HOME    | 0x05 | ESC [ H   | ESC h    | ESC [ H  |
1327   | END     | 0x06 | ESC [ F   | ESC k    | ESC [ K  |
1328   | INSERT  | 0x07 | ESC [ @   | ESC +    | ESC [ @  |
1329   |         |      | ESC [ L   |          | ESC [ L  |
1330   | DELETE  | 0x08 | ESC [ X   | ESC -    | ESC [ P  |
1331   | PG UP   | 0x09 | ESC [ I   | ESC ?    | ESC [ V  |
1332   |         |      |           |          | ESC [ ?  |
1333   | PG DOWN | 0x0A | ESC [ G   | ESC /    | ESC [ U  |
1334   |         |      |           |          | ESC [ /  |
1335   | F1      | 0x0B | ESC [ M   | ESC 1    | ESC O P  |
1336   | F2      | 0x0C | ESC [ N   | ESC 2    | ESC O Q  |
1337   | F3      | 0x0D | ESC [ O   | ESC 3    | ESC O w  |
1338   | F4      | 0x0E | ESC [ P   | ESC 4    | ESC O x  |
1339   | F5      | 0x0F | ESC [ Q   | ESC 5    | ESC O t  |
1340   | F6      | 0x10 | ESC [ R   | ESC 6    | ESC O u  |
1341   | F7      | 0x11 | ESC [ S   | ESC 7    | ESC O q  |
1342   | F8      | 0x12 | ESC [ T   | ESC 8    | ESC O r  |
1343   | F9      | 0x13 | ESC [ U   | ESC 9    | ESC O p  |
1344   | F10     | 0x14 | ESC [ V   | ESC 0    | ESC O M  |
1345   | Escape  | 0x17 | ESC       | ESC      | ESC      |
1346   | F11     | 0x15 |           | ESC !    |          |
1347   | F12     | 0x16 |           | ESC @    |          |
1348   +=========+======+===========+==========+==========+
1349 
1350   Special Mappings
1351   ================
1352   ESC R ESC r ESC R = Reset System
1353 
1354   @param TerminalDevice   The terminal device to use to translate raw input into EFI Keys
1355 
1356 **/
1357 VOID
UnicodeToEfiKey(IN TERMINAL_DEV * TerminalDevice)1358 UnicodeToEfiKey (
1359   IN  TERMINAL_DEV    *TerminalDevice
1360   )
1361 {
1362   EFI_STATUS          Status;
1363   EFI_STATUS          TimerStatus;
1364   UINT16              UnicodeChar;
1365   EFI_INPUT_KEY       Key;
1366   BOOLEAN             SetDefaultResetState;
1367 
1368   TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut);
1369 
1370   if (!EFI_ERROR (TimerStatus)) {
1371     UnicodeToEfiKeyFlushState (TerminalDevice);
1372     TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1373   }
1374 
1375   while (!IsUnicodeFiFoEmpty (TerminalDevice) && !IsEfiKeyFiFoFull (TerminalDevice)) {
1376 
1377     if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) {
1378       //
1379       // Check to see if the 2 seconds timer has expired
1380       //
1381       TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut);
1382       if (!EFI_ERROR (TimerStatus)) {
1383         UnicodeToEfiKeyFlushState (TerminalDevice);
1384         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1385       }
1386     }
1387 
1388     //
1389     // Fetch one Unicode character from the Unicode FIFO
1390     //
1391     UnicodeFiFoRemoveOneKey (TerminalDevice, &UnicodeChar);
1392 
1393     SetDefaultResetState = TRUE;
1394 
1395     switch (TerminalDevice->InputState) {
1396     case INPUT_STATE_DEFAULT:
1397 
1398       break;
1399 
1400     case INPUT_STATE_ESC:
1401 
1402       if (UnicodeChar == LEFTOPENBRACKET) {
1403         TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET;
1404         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1405         continue;
1406       }
1407 
1408       if (UnicodeChar == 'O' && (TerminalDevice->TerminalType == VT100TYPE ||
1409                                  TerminalDevice->TerminalType == TTYTERMTYPE)) {
1410         TerminalDevice->InputState |= INPUT_STATE_O;
1411         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1412         continue;
1413       }
1414 
1415       Key.ScanCode = SCAN_NULL;
1416 
1417       if (TerminalDevice->TerminalType == VT100PLUSTYPE ||
1418           TerminalDevice->TerminalType == VTUTF8TYPE) {
1419         switch (UnicodeChar) {
1420         case '1':
1421           Key.ScanCode = SCAN_F1;
1422           break;
1423         case '2':
1424           Key.ScanCode = SCAN_F2;
1425           break;
1426         case '3':
1427           Key.ScanCode = SCAN_F3;
1428           break;
1429         case '4':
1430           Key.ScanCode = SCAN_F4;
1431           break;
1432         case '5':
1433           Key.ScanCode = SCAN_F5;
1434           break;
1435         case '6':
1436           Key.ScanCode = SCAN_F6;
1437           break;
1438         case '7':
1439           Key.ScanCode = SCAN_F7;
1440           break;
1441         case '8':
1442           Key.ScanCode = SCAN_F8;
1443           break;
1444         case '9':
1445           Key.ScanCode = SCAN_F9;
1446           break;
1447         case '0':
1448           Key.ScanCode = SCAN_F10;
1449           break;
1450         case '!':
1451           Key.ScanCode = SCAN_F11;
1452           break;
1453         case '@':
1454           Key.ScanCode = SCAN_F12;
1455           break;
1456         case 'h':
1457           Key.ScanCode = SCAN_HOME;
1458           break;
1459         case 'k':
1460           Key.ScanCode = SCAN_END;
1461           break;
1462         case '+':
1463           Key.ScanCode = SCAN_INSERT;
1464           break;
1465         case '-':
1466           Key.ScanCode = SCAN_DELETE;
1467           break;
1468         case '/':
1469           Key.ScanCode = SCAN_PAGE_DOWN;
1470           break;
1471         case '?':
1472           Key.ScanCode = SCAN_PAGE_UP;
1473           break;
1474         default :
1475           break;
1476         }
1477       }
1478 
1479       switch (UnicodeChar) {
1480       case 'R':
1481         if (TerminalDevice->ResetState == RESET_STATE_DEFAULT) {
1482           TerminalDevice->ResetState = RESET_STATE_ESC_R;
1483           SetDefaultResetState = FALSE;
1484         } else if (TerminalDevice->ResetState == RESET_STATE_ESC_R_ESC_R) {
1485           gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
1486         }
1487         Key.ScanCode = SCAN_NULL;
1488         break;
1489       case 'r':
1490         if (TerminalDevice->ResetState == RESET_STATE_ESC_R) {
1491           TerminalDevice->ResetState = RESET_STATE_ESC_R_ESC_R;
1492           SetDefaultResetState = FALSE;
1493         }
1494         Key.ScanCode = SCAN_NULL;
1495         break;
1496       default :
1497         break;
1498       }
1499 
1500       if (SetDefaultResetState) {
1501         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1502       }
1503 
1504       if (Key.ScanCode != SCAN_NULL) {
1505         Key.UnicodeChar = 0;
1506         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1507         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1508         UnicodeToEfiKeyFlushState (TerminalDevice);
1509         continue;
1510       }
1511 
1512       UnicodeToEfiKeyFlushState (TerminalDevice);
1513 
1514       break;
1515 
1516     case INPUT_STATE_ESC | INPUT_STATE_O:
1517 
1518       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1519 
1520       Key.ScanCode = SCAN_NULL;
1521 
1522       if (TerminalDevice->TerminalType == VT100TYPE) {
1523         switch (UnicodeChar) {
1524         case 'P':
1525           Key.ScanCode = SCAN_F1;
1526           break;
1527         case 'Q':
1528           Key.ScanCode = SCAN_F2;
1529           break;
1530         case 'w':
1531           Key.ScanCode = SCAN_F3;
1532           break;
1533         case 'x':
1534           Key.ScanCode = SCAN_F4;
1535           break;
1536         case 't':
1537           Key.ScanCode = SCAN_F5;
1538           break;
1539         case 'u':
1540           Key.ScanCode = SCAN_F6;
1541           break;
1542         case 'q':
1543           Key.ScanCode = SCAN_F7;
1544           break;
1545         case 'r':
1546           Key.ScanCode = SCAN_F8;
1547           break;
1548         case 'p':
1549           Key.ScanCode = SCAN_F9;
1550           break;
1551         case 'M':
1552           Key.ScanCode = SCAN_F10;
1553           break;
1554         default :
1555           break;
1556         }
1557       } else if (TerminalDevice->TerminalType == TTYTERMTYPE) {
1558         /* Also accept VT100 escape codes for F1-F4, HOME and END for TTY term */
1559         switch (UnicodeChar) {
1560         case 'P':
1561           Key.ScanCode = SCAN_F1;
1562           break;
1563         case 'Q':
1564           Key.ScanCode = SCAN_F2;
1565           break;
1566         case 'R':
1567           Key.ScanCode = SCAN_F3;
1568           break;
1569         case 'S':
1570           Key.ScanCode = SCAN_F4;
1571           break;
1572         case 'H':
1573           Key.ScanCode = SCAN_HOME;
1574           break;
1575         case 'F':
1576           Key.ScanCode = SCAN_END;
1577           break;
1578         }
1579       }
1580 
1581       if (Key.ScanCode != SCAN_NULL) {
1582         Key.UnicodeChar = 0;
1583         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1584         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1585         UnicodeToEfiKeyFlushState (TerminalDevice);
1586         continue;
1587       }
1588 
1589       UnicodeToEfiKeyFlushState (TerminalDevice);
1590 
1591       break;
1592 
1593     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET:
1594 
1595       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1596 
1597       Key.ScanCode = SCAN_NULL;
1598 
1599       if (TerminalDevice->TerminalType == PCANSITYPE    ||
1600           TerminalDevice->TerminalType == VT100TYPE     ||
1601           TerminalDevice->TerminalType == VT100PLUSTYPE ||
1602           TerminalDevice->TerminalType == VTUTF8TYPE    ||
1603           TerminalDevice->TerminalType == TTYTERMTYPE) {
1604         switch (UnicodeChar) {
1605         case 'A':
1606           Key.ScanCode = SCAN_UP;
1607           break;
1608         case 'B':
1609           Key.ScanCode = SCAN_DOWN;
1610           break;
1611         case 'C':
1612           Key.ScanCode = SCAN_RIGHT;
1613           break;
1614         case 'D':
1615           Key.ScanCode = SCAN_LEFT;
1616           break;
1617         case 'H':
1618           if (TerminalDevice->TerminalType == PCANSITYPE ||
1619               TerminalDevice->TerminalType == VT100TYPE  ||
1620               TerminalDevice->TerminalType == TTYTERMTYPE) {
1621             Key.ScanCode = SCAN_HOME;
1622           }
1623           break;
1624         case 'F':
1625           if (TerminalDevice->TerminalType == PCANSITYPE ||
1626               TerminalDevice->TerminalType == TTYTERMTYPE) {
1627             Key.ScanCode = SCAN_END;
1628           }
1629           break;
1630         case 'K':
1631           if (TerminalDevice->TerminalType == VT100TYPE) {
1632             Key.ScanCode = SCAN_END;
1633           }
1634           break;
1635         case 'L':
1636         case '@':
1637           if (TerminalDevice->TerminalType == PCANSITYPE ||
1638               TerminalDevice->TerminalType == VT100TYPE) {
1639             Key.ScanCode = SCAN_INSERT;
1640           }
1641           break;
1642         case 'X':
1643           if (TerminalDevice->TerminalType == PCANSITYPE) {
1644             Key.ScanCode = SCAN_DELETE;
1645           }
1646           break;
1647         case 'P':
1648           if (TerminalDevice->TerminalType == VT100TYPE) {
1649             Key.ScanCode = SCAN_DELETE;
1650           } else if (TerminalDevice->TerminalType == PCANSITYPE) {
1651             Key.ScanCode = SCAN_F4;
1652           }
1653           break;
1654         case 'I':
1655           if (TerminalDevice->TerminalType == PCANSITYPE) {
1656             Key.ScanCode = SCAN_PAGE_UP;
1657           }
1658           break;
1659         case 'V':
1660           if (TerminalDevice->TerminalType == PCANSITYPE) {
1661             Key.ScanCode = SCAN_F10;
1662           }
1663           break;
1664         case '?':
1665           if (TerminalDevice->TerminalType == VT100TYPE) {
1666             Key.ScanCode = SCAN_PAGE_UP;
1667           }
1668           break;
1669         case 'G':
1670           if (TerminalDevice->TerminalType == PCANSITYPE) {
1671             Key.ScanCode = SCAN_PAGE_DOWN;
1672           }
1673           break;
1674         case 'U':
1675           if (TerminalDevice->TerminalType == PCANSITYPE) {
1676             Key.ScanCode = SCAN_F9;
1677           }
1678           break;
1679         case '/':
1680           if (TerminalDevice->TerminalType == VT100TYPE) {
1681             Key.ScanCode = SCAN_PAGE_DOWN;
1682           }
1683           break;
1684         case 'M':
1685           if (TerminalDevice->TerminalType == PCANSITYPE) {
1686             Key.ScanCode = SCAN_F1;
1687           }
1688           break;
1689         case 'N':
1690           if (TerminalDevice->TerminalType == PCANSITYPE) {
1691             Key.ScanCode = SCAN_F2;
1692           }
1693           break;
1694         case 'O':
1695           if (TerminalDevice->TerminalType == PCANSITYPE) {
1696             Key.ScanCode = SCAN_F3;
1697           }
1698           break;
1699         case 'Q':
1700           if (TerminalDevice->TerminalType == PCANSITYPE) {
1701             Key.ScanCode = SCAN_F5;
1702           }
1703           break;
1704         case 'R':
1705           if (TerminalDevice->TerminalType == PCANSITYPE) {
1706             Key.ScanCode = SCAN_F6;
1707           }
1708           break;
1709         case 'S':
1710           if (TerminalDevice->TerminalType == PCANSITYPE) {
1711             Key.ScanCode = SCAN_F7;
1712           }
1713           break;
1714         case 'T':
1715           if (TerminalDevice->TerminalType == PCANSITYPE) {
1716             Key.ScanCode = SCAN_F8;
1717           }
1718           break;
1719         default :
1720           break;
1721         }
1722       }
1723 
1724       /*
1725        * The VT220 escape codes that the TTY terminal accepts all have
1726        * numeric codes, and there are no ambiguous prefixes shared with
1727        * other terminal types.
1728        */
1729       if (TerminalDevice->TerminalType == TTYTERMTYPE &&
1730           Key.ScanCode == SCAN_NULL &&
1731           UnicodeChar >= '0' &&
1732           UnicodeChar <= '9') {
1733         TerminalDevice->TtyEscapeStr[0] = UnicodeChar;
1734         TerminalDevice->TtyEscapeIndex = 1;
1735         TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET_2;
1736         continue;
1737       }
1738 
1739       if (Key.ScanCode != SCAN_NULL) {
1740         Key.UnicodeChar = 0;
1741         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1742         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1743         UnicodeToEfiKeyFlushState (TerminalDevice);
1744         continue;
1745       }
1746 
1747       UnicodeToEfiKeyFlushState (TerminalDevice);
1748 
1749       break;
1750 
1751 
1752     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_LEFTOPENBRACKET_2:
1753       /*
1754        * Here we handle the VT220 escape codes that we accept.  This
1755        * state is only used by the TTY terminal type.
1756        */
1757       Key.ScanCode = SCAN_NULL;
1758       if (TerminalDevice->TerminalType == TTYTERMTYPE) {
1759 
1760         if (UnicodeChar == '~' && TerminalDevice->TtyEscapeIndex <= 2) {
1761           UINT16 EscCode;
1762           TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex] = 0; /* Terminate string */
1763           EscCode = (UINT16) StrDecimalToUintn(TerminalDevice->TtyEscapeStr);
1764           switch (EscCode) {
1765           case 2:
1766               Key.ScanCode = SCAN_INSERT;
1767               break;
1768           case 3:
1769               Key.ScanCode = SCAN_DELETE;
1770               break;
1771           case 5:
1772               Key.ScanCode = SCAN_PAGE_UP;
1773               break;
1774           case 6:
1775               Key.ScanCode = SCAN_PAGE_DOWN;
1776               break;
1777           case 11:
1778           case 12:
1779           case 13:
1780           case 14:
1781           case 15:
1782             Key.ScanCode = SCAN_F1 + EscCode - 11;
1783             break;
1784           case 17:
1785           case 18:
1786           case 19:
1787           case 20:
1788           case 21:
1789             Key.ScanCode = SCAN_F6 + EscCode - 17;
1790             break;
1791           case 23:
1792           case 24:
1793             Key.ScanCode = SCAN_F11 + EscCode - 23;
1794             break;
1795           default:
1796             break;
1797           }
1798         } else if (TerminalDevice->TtyEscapeIndex == 1){
1799           /* 2 character escape code   */
1800           TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex++] = UnicodeChar;
1801           continue;
1802         }
1803         else {
1804           DEBUG ((EFI_D_ERROR, "Unexpected state in escape2\n"));
1805         }
1806       }
1807       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1808 
1809       if (Key.ScanCode != SCAN_NULL) {
1810         Key.UnicodeChar = 0;
1811         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1812         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1813         UnicodeToEfiKeyFlushState (TerminalDevice);
1814         continue;
1815       }
1816 
1817       UnicodeToEfiKeyFlushState (TerminalDevice);
1818       break;
1819 
1820     default:
1821       //
1822       // Invalid state. This should never happen.
1823       //
1824       ASSERT (FALSE);
1825 
1826       UnicodeToEfiKeyFlushState (TerminalDevice);
1827 
1828       break;
1829     }
1830 
1831     if (UnicodeChar == ESC) {
1832       TerminalDevice->InputState = INPUT_STATE_ESC;
1833     }
1834 
1835     if (UnicodeChar == CSI) {
1836       TerminalDevice->InputState = INPUT_STATE_CSI;
1837     }
1838 
1839     if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) {
1840       Status = gBS->SetTimer(
1841                       TerminalDevice->TwoSecondTimeOut,
1842                       TimerRelative,
1843                       (UINT64)20000000
1844                       );
1845       ASSERT_EFI_ERROR (Status);
1846       continue;
1847     }
1848 
1849     if (SetDefaultResetState) {
1850       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1851     }
1852 
1853     if (UnicodeChar == DEL) {
1854       if (TerminalDevice->TerminalType == TTYTERMTYPE) {
1855         Key.ScanCode    = SCAN_NULL;
1856         Key.UnicodeChar = CHAR_BACKSPACE;
1857       }
1858       else {
1859         Key.ScanCode    = SCAN_DELETE;
1860         Key.UnicodeChar = 0;
1861       }
1862     } else {
1863       Key.ScanCode    = SCAN_NULL;
1864       Key.UnicodeChar = UnicodeChar;
1865     }
1866 
1867     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1868   }
1869 }
1870