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