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 - 2019, Intel Corporation. All rights reserved.<BR>
6 Copyright (C) 2016 Silicon Graphics, Inc. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8 
9 **/
10 
11 #include "Terminal.h"
12 
13 
14 /**
15   Reads the next keystroke from the input device. The WaitForKey Event can
16   be used to test for existence of a keystroke via WaitForEvent () call.
17 
18   @param  TerminalDevice           Terminal driver private structure
19   @param  KeyData                  A pointer to a buffer that is filled in with the
20                                    keystroke state data for the key that was
21                                    pressed.
22 
23   @retval EFI_SUCCESS              The keystroke information was returned.
24   @retval EFI_NOT_READY            There was no keystroke data available.
25   @retval EFI_INVALID_PARAMETER    KeyData is NULL.
26 
27 **/
28 EFI_STATUS
ReadKeyStrokeWorker(IN TERMINAL_DEV * TerminalDevice,OUT EFI_KEY_DATA * KeyData)29 ReadKeyStrokeWorker (
30   IN  TERMINAL_DEV *TerminalDevice,
31   OUT EFI_KEY_DATA *KeyData
32   )
33 {
34   if (KeyData == NULL) {
35     return EFI_INVALID_PARAMETER;
36   }
37 
38   KeyData->KeyState.KeyShiftState  = 0;
39   KeyData->KeyState.KeyToggleState = 0;
40 
41   if (!EfiKeyFiFoRemoveOneKey (TerminalDevice, &KeyData->Key)) {
42     return EFI_NOT_READY;
43   }
44 
45   return EFI_SUCCESS;
46 
47 }
48 
49 /**
50   Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.Reset().
51   This driver only perform dependent serial device reset regardless of
52   the value of ExtendeVerification
53 
54   @param  This                     Indicates the calling context.
55   @param  ExtendedVerification     Skip by this driver.
56 
57   @retval EFI_SUCCESS              The reset operation succeeds.
58   @retval EFI_DEVICE_ERROR         The dependent serial port reset fails.
59 
60 **/
61 EFI_STATUS
62 EFIAPI
TerminalConInReset(IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL * This,IN BOOLEAN ExtendedVerification)63 TerminalConInReset (
64   IN  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
65   IN  BOOLEAN                         ExtendedVerification
66   )
67 {
68   EFI_STATUS    Status;
69   TERMINAL_DEV  *TerminalDevice;
70 
71   TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (This);
72 
73   //
74   // Report progress code here
75   //
76   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
77     EFI_PROGRESS_CODE,
78     (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_PC_RESET),
79     TerminalDevice->DevicePath
80     );
81 
82   Status = TerminalDevice->SerialIo->Reset (TerminalDevice->SerialIo);
83 
84   //
85   // Make all the internal buffer empty for keys
86   //
87   TerminalDevice->RawFiFo->Head     = TerminalDevice->RawFiFo->Tail;
88   TerminalDevice->UnicodeFiFo->Head = TerminalDevice->UnicodeFiFo->Tail;
89   TerminalDevice->EfiKeyFiFo->Head  = TerminalDevice->EfiKeyFiFo->Tail;
90   TerminalDevice->EfiKeyFiFoForNotify->Head = TerminalDevice->EfiKeyFiFoForNotify->Tail;
91 
92   if (EFI_ERROR (Status)) {
93     REPORT_STATUS_CODE_WITH_DEVICE_PATH (
94       EFI_ERROR_CODE | EFI_ERROR_MINOR,
95       (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR),
96       TerminalDevice->DevicePath
97       );
98   }
99 
100   return Status;
101 }
102 
103 /**
104   Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke().
105 
106   @param  This                Indicates the calling context.
107   @param  Key                 A pointer to a buffer that is filled in with the
108                               keystroke information for the key that was sent
109                               from terminal.
110 
111   @retval EFI_SUCCESS         The keystroke information is returned successfully.
112   @retval EFI_NOT_READY       There is no keystroke data available.
113   @retval EFI_DEVICE_ERROR    The dependent serial device encounters error.
114 
115 **/
116 EFI_STATUS
117 EFIAPI
TerminalConInReadKeyStroke(IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL * This,OUT EFI_INPUT_KEY * Key)118 TerminalConInReadKeyStroke (
119   IN  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
120   OUT EFI_INPUT_KEY                   *Key
121   )
122 {
123   TERMINAL_DEV  *TerminalDevice;
124   EFI_STATUS    Status;
125   EFI_KEY_DATA  KeyData;
126 
127   //
128   //  get TERMINAL_DEV from "This" parameter.
129   //
130   TerminalDevice  = TERMINAL_CON_IN_DEV_FROM_THIS (This);
131 
132   Status = ReadKeyStrokeWorker (TerminalDevice, &KeyData);
133   if (EFI_ERROR (Status)) {
134     return Status;
135   }
136 
137   CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY));
138 
139   return EFI_SUCCESS;
140 
141 }
142 
143 /**
144   Check if the key already has been registered.
145 
146   If both RegsiteredData and InputData is NULL, then ASSERT().
147 
148   @param  RegsiteredData           A pointer to a buffer that is filled in with the
149                                    keystroke state data for the key that was
150                                    registered.
151   @param  InputData                A pointer to a buffer that is filled in with the
152                                    keystroke state data for the key that was
153                                    pressed.
154 
155   @retval TRUE                     Key be pressed matches a registered key.
156   @retval FALSE                    Match failed.
157 
158 **/
159 BOOLEAN
IsKeyRegistered(IN EFI_KEY_DATA * RegsiteredData,IN EFI_KEY_DATA * InputData)160 IsKeyRegistered (
161   IN EFI_KEY_DATA  *RegsiteredData,
162   IN EFI_KEY_DATA  *InputData
163   )
164 {
165   ASSERT (RegsiteredData != NULL && InputData != NULL);
166 
167   if ((RegsiteredData->Key.ScanCode    != InputData->Key.ScanCode) ||
168       (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) {
169     return FALSE;
170   }
171 
172   return TRUE;
173 }
174 
175 
176 
177 /**
178   Event notification function for EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event
179   Signal the event if there is key available
180 
181   @param  Event                    Indicates the event that invoke this function.
182   @param  Context                  Indicates the calling context.
183 
184 **/
185 VOID
186 EFIAPI
TerminalConInWaitForKeyEx(IN EFI_EVENT Event,IN VOID * Context)187 TerminalConInWaitForKeyEx (
188   IN  EFI_EVENT       Event,
189   IN  VOID            *Context
190   )
191 {
192   TerminalConInWaitForKey (Event, Context);
193 }
194 
195 //
196 // Simple Text Input Ex protocol functions
197 //
198 
199 /**
200   Reset the input device and optionally run diagnostics
201 
202   @param  This                     Protocol instance pointer.
203   @param  ExtendedVerification     Driver may perform diagnostics on reset.
204 
205   @retval EFI_SUCCESS              The device was reset.
206   @retval EFI_DEVICE_ERROR         The device is not functioning properly and could
207                                    not be reset.
208 
209 **/
210 EFI_STATUS
211 EFIAPI
TerminalConInResetEx(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN BOOLEAN ExtendedVerification)212 TerminalConInResetEx (
213   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
214   IN BOOLEAN                            ExtendedVerification
215   )
216 {
217   EFI_STATUS              Status;
218   TERMINAL_DEV            *TerminalDevice;
219 
220   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
221 
222   Status = TerminalDevice->SimpleInput.Reset (&TerminalDevice->SimpleInput, ExtendedVerification);
223   if (EFI_ERROR (Status)) {
224     return EFI_DEVICE_ERROR;
225   }
226 
227   return EFI_SUCCESS;
228 
229 }
230 
231 
232 /**
233   Reads the next keystroke from the input device. The WaitForKey Event can
234   be used to test for existence of a keystroke via WaitForEvent () call.
235 
236   @param  This                     Protocol instance pointer.
237   @param  KeyData                  A pointer to a buffer that is filled in with the
238                                    keystroke state data for the key that was
239                                    pressed.
240 
241   @retval EFI_SUCCESS              The keystroke information was returned.
242   @retval EFI_NOT_READY            There was no keystroke data available.
243   @retval EFI_DEVICE_ERROR         The keystroke information was not returned due
244                                    to hardware errors.
245   @retval EFI_INVALID_PARAMETER    KeyData is NULL.
246 
247 **/
248 EFI_STATUS
249 EFIAPI
TerminalConInReadKeyStrokeEx(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,OUT EFI_KEY_DATA * KeyData)250 TerminalConInReadKeyStrokeEx (
251   IN  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
252   OUT EFI_KEY_DATA                      *KeyData
253   )
254 {
255   TERMINAL_DEV                    *TerminalDevice;
256 
257   if (KeyData == NULL) {
258     return EFI_INVALID_PARAMETER;
259   }
260 
261   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
262 
263   return ReadKeyStrokeWorker (TerminalDevice, KeyData);
264 
265 }
266 
267 
268 /**
269   Set certain state for the input device.
270 
271   @param  This                     Protocol instance pointer.
272   @param  KeyToggleState           A pointer to the EFI_KEY_TOGGLE_STATE to set the
273                                    state for the input device.
274 
275   @retval EFI_SUCCESS              The device state was set successfully.
276   @retval EFI_DEVICE_ERROR         The device is not functioning correctly and
277                                    could not have the setting adjusted.
278   @retval EFI_UNSUPPORTED          The device does not have the ability to set its
279                                    state.
280   @retval EFI_INVALID_PARAMETER    KeyToggleState is NULL.
281 
282 **/
283 EFI_STATUS
284 EFIAPI
TerminalConInSetState(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN EFI_KEY_TOGGLE_STATE * KeyToggleState)285 TerminalConInSetState (
286   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
287   IN EFI_KEY_TOGGLE_STATE               *KeyToggleState
288   )
289 {
290   if (KeyToggleState == NULL) {
291     return EFI_INVALID_PARAMETER;
292   }
293 
294   if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) {
295     return EFI_UNSUPPORTED;
296   }
297 
298   return EFI_SUCCESS;
299 }
300 
301 
302 /**
303   Register a notification function for a particular keystroke for the input device.
304 
305   @param  This                     Protocol instance pointer.
306   @param  KeyData                  A pointer to a buffer that is filled in with
307                                    the keystroke information for the key that was
308                                    pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState
309                                    and KeyData.KeyState.KeyShiftState are 0, then any incomplete
310                                    keystroke will trigger a notification of the KeyNotificationFunction.
311   @param  KeyNotificationFunction  Points to the function to be called when the key
312                                    sequence is typed specified by KeyData. This notification function
313                                    should be called at <=TPL_CALLBACK.
314   @param  NotifyHandle             Points to the unique handle assigned to the
315                                    registered notification.
316 
317   @retval EFI_SUCCESS              The notification function was registered
318                                    successfully.
319   @retval EFI_OUT_OF_RESOURCES     Unable to allocate resources for necessary data
320                                    structures.
321   @retval EFI_INVALID_PARAMETER    KeyData or NotifyHandle is NULL.
322 
323 **/
324 EFI_STATUS
325 EFIAPI
TerminalConInRegisterKeyNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN EFI_KEY_DATA * KeyData,IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction,OUT VOID ** NotifyHandle)326 TerminalConInRegisterKeyNotify (
327   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
328   IN EFI_KEY_DATA                       *KeyData,
329   IN EFI_KEY_NOTIFY_FUNCTION            KeyNotificationFunction,
330   OUT VOID                              **NotifyHandle
331   )
332 {
333   TERMINAL_DEV                    *TerminalDevice;
334   TERMINAL_CONSOLE_IN_EX_NOTIFY   *NewNotify;
335   LIST_ENTRY                      *Link;
336   LIST_ENTRY                      *NotifyList;
337   TERMINAL_CONSOLE_IN_EX_NOTIFY   *CurrentNotify;
338 
339   if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) {
340     return EFI_INVALID_PARAMETER;
341   }
342 
343   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
344 
345   //
346   // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered.
347   //
348   NotifyList = &TerminalDevice->NotifyList;
349   for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) {
350     CurrentNotify = CR (
351                       Link,
352                       TERMINAL_CONSOLE_IN_EX_NOTIFY,
353                       NotifyEntry,
354                       TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
355                       );
356     if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
357       if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) {
358         *NotifyHandle = CurrentNotify;
359         return EFI_SUCCESS;
360       }
361     }
362   }
363 
364   //
365   // Allocate resource to save the notification function
366   //
367   NewNotify = (TERMINAL_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (TERMINAL_CONSOLE_IN_EX_NOTIFY));
368   if (NewNotify == NULL) {
369     return EFI_OUT_OF_RESOURCES;
370   }
371 
372   NewNotify->Signature         = TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE;
373   NewNotify->KeyNotificationFn = KeyNotificationFunction;
374   CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
375   InsertTailList (&TerminalDevice->NotifyList, &NewNotify->NotifyEntry);
376 
377   *NotifyHandle                = NewNotify;
378 
379   return EFI_SUCCESS;
380 }
381 
382 
383 /**
384   Remove a registered notification function from a particular keystroke.
385 
386   @param  This                     Protocol instance pointer.
387   @param  NotificationHandle       The handle of the notification function being
388                                    unregistered.
389 
390   @retval EFI_SUCCESS              The notification function was unregistered
391                                    successfully.
392   @retval EFI_INVALID_PARAMETER    The NotificationHandle is invalid.
393 
394 **/
395 EFI_STATUS
396 EFIAPI
TerminalConInUnregisterKeyNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * This,IN VOID * NotificationHandle)397 TerminalConInUnregisterKeyNotify (
398   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
399   IN VOID                               *NotificationHandle
400   )
401 {
402   TERMINAL_DEV                    *TerminalDevice;
403   LIST_ENTRY                      *Link;
404   TERMINAL_CONSOLE_IN_EX_NOTIFY   *CurrentNotify;
405   LIST_ENTRY                      *NotifyList;
406 
407   if (NotificationHandle == NULL) {
408     return EFI_INVALID_PARAMETER;
409   }
410 
411   TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This);
412 
413   NotifyList = &TerminalDevice->NotifyList;
414   for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) {
415     CurrentNotify = CR (
416                       Link,
417                       TERMINAL_CONSOLE_IN_EX_NOTIFY,
418                       NotifyEntry,
419                       TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
420                       );
421     if (CurrentNotify == NotificationHandle) {
422       //
423       // Remove the notification function from NotifyList and free resources
424       //
425       RemoveEntryList (&CurrentNotify->NotifyEntry);
426 
427       gBS->FreePool (CurrentNotify);
428       return EFI_SUCCESS;
429     }
430   }
431 
432   //
433   // Can not find the matching entry in database.
434   //
435   return EFI_INVALID_PARAMETER;
436 }
437 
438 /**
439   Translate raw data into Unicode (according to different encode), and
440   translate Unicode into key information. (according to different standard).
441 
442   @param  TerminalDevice       Terminal driver private structure.
443 
444 **/
445 VOID
TranslateRawDataToEfiKey(IN TERMINAL_DEV * TerminalDevice)446 TranslateRawDataToEfiKey (
447   IN  TERMINAL_DEV      *TerminalDevice
448   )
449 {
450   switch (TerminalDevice->TerminalType) {
451 
452   case TerminalTypePcAnsi:
453   case TerminalTypeVt100:
454   case TerminalTypeVt100Plus:
455   case TerminalTypeTtyTerm:
456   case TerminalTypeLinux:
457   case TerminalTypeXtermR6:
458   case TerminalTypeVt400:
459   case TerminalTypeSCO:
460     AnsiRawDataToUnicode (TerminalDevice);
461     UnicodeToEfiKey (TerminalDevice);
462     break;
463 
464   case TerminalTypeVtUtf8:
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       break;
989     }
990   }
991   if (IsEfiKeyFiFoFull (TerminalDevice)) {
992     //
993     // Efi Key FIFO is full
994     //
995     return FALSE;
996   }
997 
998   CopyMem (&TerminalDevice->EfiKeyFiFo->Data[Tail], Key, sizeof (EFI_INPUT_KEY));
999 
1000   TerminalDevice->EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
1001 
1002   return TRUE;
1003 }
1004 
1005 /**
1006   Remove one pre-fetched key out of the FIFO buffer.
1007 
1008   @param  TerminalDevice       Terminal driver private structure.
1009   @param  Output               The key will be removed.
1010 
1011   @retval TRUE                 If insert successfully.
1012   @retval FALSE                If FIFO buffer is empty before remove operation.
1013 
1014 **/
1015 BOOLEAN
EfiKeyFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,EFI_INPUT_KEY * Output)1016 EfiKeyFiFoRemoveOneKey (
1017   TERMINAL_DEV  *TerminalDevice,
1018   EFI_INPUT_KEY *Output
1019   )
1020 {
1021   UINT8 Head;
1022 
1023   Head = TerminalDevice->EfiKeyFiFo->Head;
1024   ASSERT (Head < FIFO_MAX_NUMBER + 1);
1025 
1026   if (IsEfiKeyFiFoEmpty (TerminalDevice)) {
1027     //
1028     //  FIFO is empty
1029     //
1030     Output->ScanCode    = SCAN_NULL;
1031     Output->UnicodeChar = 0;
1032     return FALSE;
1033   }
1034 
1035   CopyMem (Output, &TerminalDevice->EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY));
1036 
1037   TerminalDevice->EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
1038 
1039   return TRUE;
1040 }
1041 
1042 /**
1043   Clarify whether FIFO buffer is empty.
1044 
1045   @param  TerminalDevice       Terminal driver private structure
1046 
1047   @retval TRUE                 If FIFO buffer is empty.
1048   @retval FALSE                If FIFO buffer is not empty.
1049 
1050 **/
1051 BOOLEAN
IsEfiKeyFiFoEmpty(TERMINAL_DEV * TerminalDevice)1052 IsEfiKeyFiFoEmpty (
1053   TERMINAL_DEV  *TerminalDevice
1054   )
1055 {
1056   if (TerminalDevice->EfiKeyFiFo->Head == TerminalDevice->EfiKeyFiFo->Tail) {
1057     return TRUE;
1058   } else {
1059     return FALSE;
1060   }
1061 }
1062 
1063 /**
1064   Clarify whether FIFO buffer is full.
1065 
1066   @param  TerminalDevice       Terminal driver private structure
1067 
1068   @retval TRUE                 If FIFO buffer is full.
1069   @retval FALSE                If FIFO buffer is not full.
1070 
1071 **/
1072 BOOLEAN
IsEfiKeyFiFoFull(TERMINAL_DEV * TerminalDevice)1073 IsEfiKeyFiFoFull (
1074   TERMINAL_DEV  *TerminalDevice
1075   )
1076 {
1077   UINT8 Tail;
1078   UINT8 Head;
1079 
1080   Tail  = TerminalDevice->EfiKeyFiFo->Tail;
1081   Head  = TerminalDevice->EfiKeyFiFo->Head;
1082 
1083   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
1084 
1085     return TRUE;
1086   }
1087 
1088   return FALSE;
1089 }
1090 
1091 /**
1092   Insert one pre-fetched key into the Unicode FIFO buffer.
1093 
1094   @param  TerminalDevice       Terminal driver private structure.
1095   @param  Input                The key will be input.
1096 
1097   @retval TRUE                 If insert successfully.
1098   @retval FALSE                If Unicode FIFO buffer is full before key insertion,
1099                                and the key is lost.
1100 
1101 **/
1102 BOOLEAN
UnicodeFiFoInsertOneKey(TERMINAL_DEV * TerminalDevice,UINT16 Input)1103 UnicodeFiFoInsertOneKey (
1104   TERMINAL_DEV      *TerminalDevice,
1105   UINT16            Input
1106   )
1107 {
1108   UINT8 Tail;
1109 
1110   Tail = TerminalDevice->UnicodeFiFo->Tail;
1111   ASSERT (Tail < FIFO_MAX_NUMBER + 1);
1112 
1113 
1114   if (IsUnicodeFiFoFull (TerminalDevice)) {
1115     //
1116     // Unicode FIFO is full
1117     //
1118     return FALSE;
1119   }
1120 
1121   TerminalDevice->UnicodeFiFo->Data[Tail]  = Input;
1122 
1123   TerminalDevice->UnicodeFiFo->Tail        = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
1124 
1125   return TRUE;
1126 }
1127 
1128 /**
1129   Remove one pre-fetched key out of the Unicode FIFO buffer.
1130   The caller should guarantee that Unicode FIFO buffer is not empty
1131   by IsUnicodeFiFoEmpty ().
1132 
1133   @param  TerminalDevice       Terminal driver private structure.
1134   @param  Output               The key will be removed.
1135 
1136 **/
1137 VOID
UnicodeFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,UINT16 * Output)1138 UnicodeFiFoRemoveOneKey (
1139   TERMINAL_DEV  *TerminalDevice,
1140   UINT16        *Output
1141   )
1142 {
1143   UINT8 Head;
1144 
1145   Head = TerminalDevice->UnicodeFiFo->Head;
1146   ASSERT (Head < FIFO_MAX_NUMBER + 1);
1147 
1148   *Output = TerminalDevice->UnicodeFiFo->Data[Head];
1149 
1150   TerminalDevice->UnicodeFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
1151 }
1152 
1153 /**
1154   Clarify whether Unicode FIFO buffer is empty.
1155 
1156   @param  TerminalDevice       Terminal driver private structure
1157 
1158   @retval TRUE                 If Unicode FIFO buffer is empty.
1159   @retval FALSE                If Unicode FIFO buffer is not empty.
1160 
1161 **/
1162 BOOLEAN
IsUnicodeFiFoEmpty(TERMINAL_DEV * TerminalDevice)1163 IsUnicodeFiFoEmpty (
1164   TERMINAL_DEV  *TerminalDevice
1165   )
1166 {
1167   if (TerminalDevice->UnicodeFiFo->Head == TerminalDevice->UnicodeFiFo->Tail) {
1168     return TRUE;
1169   } else {
1170     return FALSE;
1171   }
1172 }
1173 
1174 /**
1175   Clarify whether Unicode FIFO buffer is full.
1176 
1177   @param  TerminalDevice       Terminal driver private structure
1178 
1179   @retval TRUE                 If Unicode FIFO buffer is full.
1180   @retval FALSE                If Unicode FIFO buffer is not full.
1181 
1182 **/
1183 BOOLEAN
IsUnicodeFiFoFull(TERMINAL_DEV * TerminalDevice)1184 IsUnicodeFiFoFull (
1185   TERMINAL_DEV  *TerminalDevice
1186   )
1187 {
1188   UINT8 Tail;
1189   UINT8 Head;
1190 
1191   Tail  = TerminalDevice->UnicodeFiFo->Tail;
1192   Head  = TerminalDevice->UnicodeFiFo->Head;
1193 
1194   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
1195 
1196     return TRUE;
1197   }
1198 
1199   return FALSE;
1200 }
1201 
1202 
1203 /**
1204   Update the Unicode characters from a terminal input device into EFI Keys FIFO.
1205 
1206   @param TerminalDevice   The terminal device to use to translate raw input into EFI Keys
1207 
1208 **/
1209 VOID
UnicodeToEfiKeyFlushState(IN TERMINAL_DEV * TerminalDevice)1210 UnicodeToEfiKeyFlushState (
1211   IN  TERMINAL_DEV    *TerminalDevice
1212   )
1213 {
1214   EFI_INPUT_KEY Key;
1215   UINT32        InputState;
1216 
1217   InputState = TerminalDevice->InputState;
1218 
1219   if (IsEfiKeyFiFoFull (TerminalDevice)) {
1220     return;
1221   }
1222 
1223   if ((InputState & INPUT_STATE_ESC) != 0) {
1224     Key.ScanCode    = SCAN_ESC;
1225     Key.UnicodeChar = 0;
1226     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1227   }
1228 
1229   if ((InputState & INPUT_STATE_CSI) != 0) {
1230     Key.ScanCode    = SCAN_NULL;
1231     Key.UnicodeChar = CSI;
1232     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1233   }
1234 
1235   if ((InputState & INPUT_STATE_LEFTOPENBRACKET) != 0) {
1236     Key.ScanCode    = SCAN_NULL;
1237     Key.UnicodeChar = LEFTOPENBRACKET;
1238     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1239   }
1240 
1241   if ((InputState & INPUT_STATE_O) != 0) {
1242     Key.ScanCode    = SCAN_NULL;
1243     Key.UnicodeChar = 'O';
1244     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1245   }
1246 
1247   if ((InputState & INPUT_STATE_2) != 0) {
1248     Key.ScanCode    = SCAN_NULL;
1249     Key.UnicodeChar = '2';
1250     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1251   }
1252 
1253   //
1254   // Cancel the timer.
1255   //
1256   gBS->SetTimer (
1257         TerminalDevice->TwoSecondTimeOut,
1258         TimerCancel,
1259         0
1260         );
1261 
1262   TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1263 }
1264 
1265 
1266 /**
1267   Converts a stream of Unicode characters from a terminal input device into EFI Keys that
1268   can be read through the Simple Input Protocol.
1269 
1270   The table below shows the keyboard input mappings that this function supports.
1271   If the ESC sequence listed in one of the columns is presented, then it is translated
1272   into the corresponding EFI Scan Code.  If a matching sequence is not found, then the raw
1273   key strokes are converted into EFI Keys.
1274 
1275   2 seconds are allowed for an ESC sequence to be completed.  If the ESC sequence is not
1276   completed in 2 seconds, then the raw key strokes of the partial ESC sequence are
1277   converted into EFI Keys.
1278   There is one special input sequence that will force the system to reset.
1279   This is ESC R ESC r ESC R.
1280 
1281   Note: current implementation support terminal types include: PC ANSI, VT100+/VTUTF8, VT100.
1282         The table below is not same with UEFI Spec 2.3 Appendix B Table 201(not support ANSI X3.64 /
1283         DEC VT200-500 and extra support PC ANSI, VT100)since UEFI Table 201 is just an example.
1284 
1285   Symbols used in table below
1286   ===========================
1287     ESC = 0x1B
1288     CSI = 0x9B
1289     DEL = 0x7f
1290     ^   = CTRL
1291 
1292   +=========+======+===========+==========+==========+
1293   |         | EFI  | UEFI 2.0  |          |          |
1294   |         | Scan |           |  VT100+  |          |
1295   |   KEY   | Code |  PC ANSI  |  VTUTF8  |   VT100  |
1296   +=========+======+===========+==========+==========+
1297   | NULL    | 0x00 |           |          |          |
1298   | UP      | 0x01 | ESC [ A   | ESC [ A  | ESC [ A  |
1299   | DOWN    | 0x02 | ESC [ B   | ESC [ B  | ESC [ B  |
1300   | RIGHT   | 0x03 | ESC [ C   | ESC [ C  | ESC [ C  |
1301   | LEFT    | 0x04 | ESC [ D   | ESC [ D  | ESC [ D  |
1302   | HOME    | 0x05 | ESC [ H   | ESC h    | ESC [ H  |
1303   | END     | 0x06 | ESC [ F   | ESC k    | ESC [ K  |
1304   | INSERT  | 0x07 | ESC [ @   | ESC +    | ESC [ @  |
1305   |         |      | ESC [ L   |          | ESC [ L  |
1306   | DELETE  | 0x08 | ESC [ X   | ESC -    | ESC [ P  |
1307   | PG UP   | 0x09 | ESC [ I   | ESC ?    | ESC [ V  |
1308   |         |      |           |          | ESC [ ?  |
1309   | PG DOWN | 0x0A | ESC [ G   | ESC /    | ESC [ U  |
1310   |         |      |           |          | ESC [ /  |
1311   | F1      | 0x0B | ESC [ M   | ESC 1    | ESC O P  |
1312   | F2      | 0x0C | ESC [ N   | ESC 2    | ESC O Q  |
1313   | F3      | 0x0D | ESC [ O   | ESC 3    | ESC O w  |
1314   | F4      | 0x0E | ESC [ P   | ESC 4    | ESC O x  |
1315   | F5      | 0x0F | ESC [ Q   | ESC 5    | ESC O t  |
1316   | F6      | 0x10 | ESC [ R   | ESC 6    | ESC O u  |
1317   | F7      | 0x11 | ESC [ S   | ESC 7    | ESC O q  |
1318   | F8      | 0x12 | ESC [ T   | ESC 8    | ESC O r  |
1319   | F9      | 0x13 | ESC [ U   | ESC 9    | ESC O p  |
1320   | F10     | 0x14 | ESC [ V   | ESC 0    | ESC O M  |
1321   | Escape  | 0x17 | ESC       | ESC      | ESC      |
1322   | F11     | 0x15 |           | ESC !    |          |
1323   | F12     | 0x16 |           | ESC @    |          |
1324   +=========+======+===========+==========+==========+
1325 
1326 Putty function key map:
1327   +=========+======+===========+=============+=============+=============+=========+
1328   |         | EFI  |           |             |             |             |         |
1329   |         | Scan |           |             |  Normal     |             |         |
1330   |   KEY   | Code |  VT100+   | Xterm R6    |  VT400      | Linux       | SCO     |
1331   +=========+======+===========+=============+=============+=============+=========+
1332   | F1      | 0x0B | ESC O P   | ESC O P     | ESC [ 1 1 ~ | ESC [ [ A   | ESC [ M |
1333   | F2      | 0x0C | ESC O Q   | ESC O Q     | ESC [ 1 2 ~ | ESC [ [ B   | ESC [ N |
1334   | F3      | 0x0D | ESC O R   | ESC O R     | ESC [ 1 3 ~ | ESC [ [ C   | ESC [ O |
1335   | F4      | 0x0E | ESC O S   | ESC O S     | ESC [ 1 4 ~ | ESC [ [ D   | ESC [ P |
1336   | F5      | 0x0F | ESC O T   | ESC [ 1 5 ~ | ESC [ 1 5 ~ | ESC [ [ E   | ESC [ Q |
1337   | F6      | 0x10 | ESC O U   | ESC [ 1 7 ~ | ESC [ 1 7 ~ | ESC [ 1 7 ~ | ESC [ R |
1338   | F7      | 0x11 | ESC O V   | ESC [ 1 8 ~ | ESC [ 1 8 ~ | ESC [ 1 8 ~ | ESC [ S |
1339   | F8      | 0x12 | ESC O W   | ESC [ 1 9 ~ | ESC [ 1 9 ~ | ESC [ 1 9 ~ | ESC [ T |
1340   | F9      | 0x13 | ESC O X   | ESC [ 2 0 ~ | ESC [ 2 0 ~ | ESC [ 2 0 ~ | ESC [ U |
1341   | F10     | 0x14 | ESC O Y   | ESC [ 2 1 ~ | ESC [ 2 1 ~ | ESC [ 2 1 ~ | ESC [ V |
1342   | Escape  | 0x17 | ESC       | ESC         | ESC         | ESC         | ESC     |
1343   | F11     | 0x15 | ESC O Z   | ESC [ 2 3 ~ | ESC [ 2 3 ~ | ESC [ 2 3 ~ | ESC [ W |
1344   | F12     | 0x16 | ESC O [   | ESC [ 2 4 ~ | ESC [ 2 4 ~ | ESC [ 2 4 ~ | ESC [ X |
1345   +=========+======+===========+=============+=============+=============+=========+
1346 
1347   Special Mappings
1348   ================
1349   ESC R ESC r ESC R = Reset System
1350 
1351   @param TerminalDevice   The terminal device to use to translate raw input into EFI Keys
1352 
1353 **/
1354 VOID
UnicodeToEfiKey(IN TERMINAL_DEV * TerminalDevice)1355 UnicodeToEfiKey (
1356   IN  TERMINAL_DEV    *TerminalDevice
1357   )
1358 {
1359   EFI_STATUS          Status;
1360   EFI_STATUS          TimerStatus;
1361   UINT16              UnicodeChar;
1362   EFI_INPUT_KEY       Key;
1363   BOOLEAN             SetDefaultResetState;
1364 
1365   TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut);
1366 
1367   if (!EFI_ERROR (TimerStatus)) {
1368     UnicodeToEfiKeyFlushState (TerminalDevice);
1369     TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1370   }
1371 
1372   while (!IsUnicodeFiFoEmpty (TerminalDevice) && !IsEfiKeyFiFoFull (TerminalDevice)) {
1373 
1374     if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) {
1375       //
1376       // Check to see if the 2 seconds timer has expired
1377       //
1378       TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut);
1379       if (!EFI_ERROR (TimerStatus)) {
1380         UnicodeToEfiKeyFlushState (TerminalDevice);
1381         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1382       }
1383     }
1384 
1385     //
1386     // Fetch one Unicode character from the Unicode FIFO
1387     //
1388     UnicodeFiFoRemoveOneKey (TerminalDevice, &UnicodeChar);
1389 
1390     SetDefaultResetState = TRUE;
1391 
1392     switch (TerminalDevice->InputState) {
1393     case INPUT_STATE_DEFAULT:
1394 
1395       break;
1396 
1397     case INPUT_STATE_ESC:
1398 
1399       if (UnicodeChar == LEFTOPENBRACKET) {
1400         TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET;
1401         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1402         continue;
1403       }
1404 
1405       if (UnicodeChar == 'O' && (TerminalDevice->TerminalType == TerminalTypeVt100 ||
1406                                  TerminalDevice->TerminalType == TerminalTypeTtyTerm ||
1407                                  TerminalDevice->TerminalType == TerminalTypeXtermR6 ||
1408                                  TerminalDevice->TerminalType == TerminalTypeVt100Plus)) {
1409         TerminalDevice->InputState |= INPUT_STATE_O;
1410         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1411         continue;
1412       }
1413 
1414       Key.ScanCode = SCAN_NULL;
1415 
1416       if (TerminalDevice->TerminalType == TerminalTypeVt100Plus ||
1417           TerminalDevice->TerminalType == TerminalTypeVtUtf8) {
1418         switch (UnicodeChar) {
1419         case '1':
1420           Key.ScanCode = SCAN_F1;
1421           break;
1422         case '2':
1423           Key.ScanCode = SCAN_F2;
1424           break;
1425         case '3':
1426           Key.ScanCode = SCAN_F3;
1427           break;
1428         case '4':
1429           Key.ScanCode = SCAN_F4;
1430           break;
1431         case '5':
1432           Key.ScanCode = SCAN_F5;
1433           break;
1434         case '6':
1435           Key.ScanCode = SCAN_F6;
1436           break;
1437         case '7':
1438           Key.ScanCode = SCAN_F7;
1439           break;
1440         case '8':
1441           Key.ScanCode = SCAN_F8;
1442           break;
1443         case '9':
1444           Key.ScanCode = SCAN_F9;
1445           break;
1446         case '0':
1447           Key.ScanCode = SCAN_F10;
1448           break;
1449         case '!':
1450           Key.ScanCode = SCAN_F11;
1451           break;
1452         case '@':
1453           Key.ScanCode = SCAN_F12;
1454           break;
1455         case 'h':
1456           Key.ScanCode = SCAN_HOME;
1457           break;
1458         case 'k':
1459           Key.ScanCode = SCAN_END;
1460           break;
1461         case '+':
1462           Key.ScanCode = SCAN_INSERT;
1463           break;
1464         case '-':
1465           Key.ScanCode = SCAN_DELETE;
1466           break;
1467         case '/':
1468           Key.ScanCode = SCAN_PAGE_DOWN;
1469           break;
1470         case '?':
1471           Key.ScanCode = SCAN_PAGE_UP;
1472           break;
1473         default :
1474           break;
1475         }
1476       }
1477 
1478       switch (UnicodeChar) {
1479       case 'R':
1480         if (TerminalDevice->ResetState == RESET_STATE_DEFAULT) {
1481           TerminalDevice->ResetState = RESET_STATE_ESC_R;
1482           SetDefaultResetState = FALSE;
1483         } else if (TerminalDevice->ResetState == RESET_STATE_ESC_R_ESC_R) {
1484           gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
1485         }
1486         Key.ScanCode = SCAN_NULL;
1487         break;
1488       case 'r':
1489         if (TerminalDevice->ResetState == RESET_STATE_ESC_R) {
1490           TerminalDevice->ResetState = RESET_STATE_ESC_R_ESC_R;
1491           SetDefaultResetState = FALSE;
1492         }
1493         Key.ScanCode = SCAN_NULL;
1494         break;
1495       default :
1496         break;
1497       }
1498 
1499       if (SetDefaultResetState) {
1500         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1501       }
1502 
1503       if (Key.ScanCode != SCAN_NULL) {
1504         Key.UnicodeChar = 0;
1505         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1506         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1507         UnicodeToEfiKeyFlushState (TerminalDevice);
1508         continue;
1509       }
1510 
1511       UnicodeToEfiKeyFlushState (TerminalDevice);
1512 
1513       break;
1514 
1515     case INPUT_STATE_ESC | INPUT_STATE_O:
1516 
1517       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1518 
1519       Key.ScanCode = SCAN_NULL;
1520 
1521       if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1522         switch (UnicodeChar) {
1523         case 'P':
1524           Key.ScanCode = SCAN_F1;
1525           break;
1526         case 'Q':
1527           Key.ScanCode = SCAN_F2;
1528           break;
1529         case 'w':
1530           Key.ScanCode = SCAN_F3;
1531           break;
1532         case 'x':
1533           Key.ScanCode = SCAN_F4;
1534           break;
1535         case 't':
1536           Key.ScanCode = SCAN_F5;
1537           break;
1538         case 'u':
1539           Key.ScanCode = SCAN_F6;
1540           break;
1541         case 'q':
1542           Key.ScanCode = SCAN_F7;
1543           break;
1544         case 'r':
1545           Key.ScanCode = SCAN_F8;
1546           break;
1547         case 'p':
1548           Key.ScanCode = SCAN_F9;
1549           break;
1550         case 'M':
1551           Key.ScanCode = SCAN_F10;
1552           break;
1553         default :
1554           break;
1555         }
1556       } else if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1557         /* Also accept VT100 escape codes for F1-F4, HOME and END for TTY term */
1558         switch (UnicodeChar) {
1559         case 'P':
1560           Key.ScanCode = SCAN_F1;
1561           break;
1562         case 'Q':
1563           Key.ScanCode = SCAN_F2;
1564           break;
1565         case 'R':
1566           Key.ScanCode = SCAN_F3;
1567           break;
1568         case 'S':
1569           Key.ScanCode = SCAN_F4;
1570           break;
1571         case 'H':
1572           Key.ScanCode = SCAN_HOME;
1573           break;
1574         case 'F':
1575           Key.ScanCode = SCAN_END;
1576           break;
1577         }
1578       } else if (TerminalDevice->TerminalType == TerminalTypeVt100Plus) {
1579         switch (UnicodeChar) {
1580         case 'P':
1581           Key.ScanCode = SCAN_F1;
1582           break;
1583         case 'Q':
1584           Key.ScanCode = SCAN_F2;
1585           break;
1586         case 'R':
1587           Key.ScanCode = SCAN_F3;
1588           break;
1589         case 'S':
1590           Key.ScanCode = SCAN_F4;
1591           break;
1592         case 'T':
1593           Key.ScanCode = SCAN_F5;
1594           break;
1595         case 'U':
1596           Key.ScanCode = SCAN_F6;
1597           break;
1598         case 'V':
1599           Key.ScanCode = SCAN_F7;
1600           break;
1601         case 'W':
1602           Key.ScanCode = SCAN_F8;
1603           break;
1604         case 'X':
1605           Key.ScanCode = SCAN_F9;
1606           break;
1607         case 'Y':
1608           Key.ScanCode = SCAN_F10;
1609           break;
1610         case 'Z':
1611           Key.ScanCode = SCAN_F11;
1612           break;
1613         case '[':
1614           Key.ScanCode = SCAN_F12;
1615           break;
1616         }
1617       } else if (TerminalDevice->TerminalType == TerminalTypeXtermR6) {
1618         switch (UnicodeChar) {
1619         case 'P':
1620           Key.ScanCode = SCAN_F1;
1621           break;
1622         case 'Q':
1623           Key.ScanCode = SCAN_F2;
1624           break;
1625         case 'R':
1626           Key.ScanCode = SCAN_F3;
1627           break;
1628         case 'S':
1629           Key.ScanCode = SCAN_F4;
1630           break;
1631         }
1632       }
1633 
1634       if (Key.ScanCode != SCAN_NULL) {
1635         Key.UnicodeChar = 0;
1636         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1637         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1638         UnicodeToEfiKeyFlushState (TerminalDevice);
1639         continue;
1640       }
1641 
1642       UnicodeToEfiKeyFlushState (TerminalDevice);
1643 
1644       break;
1645 
1646     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET:
1647 
1648       if (UnicodeChar == '1' && (TerminalDevice->TerminalType == TerminalTypeXtermR6 ||
1649                                   TerminalDevice->TerminalType == TerminalTypeVt400 ||
1650                                   TerminalDevice->TerminalType == TerminalTypeLinux)) {
1651         TerminalDevice->InputState |= INPUT_STATE_1;
1652         continue;
1653       }
1654 
1655       if (UnicodeChar == '2' && (TerminalDevice->TerminalType == TerminalTypeXtermR6 ||
1656                                   TerminalDevice->TerminalType == TerminalTypeVt400 ||
1657                                   TerminalDevice->TerminalType == TerminalTypeLinux)) {
1658         TerminalDevice->InputState |= INPUT_STATE_2;
1659         continue;
1660       }
1661 
1662       if (UnicodeChar == LEFTOPENBRACKET && TerminalDevice->TerminalType == TerminalTypeLinux) {
1663         TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET_2ND;
1664         continue;
1665       }
1666 
1667       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1668 
1669       Key.ScanCode = SCAN_NULL;
1670 
1671       if (TerminalDevice->TerminalType == TerminalTypePcAnsi    ||
1672           TerminalDevice->TerminalType == TerminalTypeVt100     ||
1673           TerminalDevice->TerminalType == TerminalTypeVt100Plus ||
1674           TerminalDevice->TerminalType == TerminalTypeVtUtf8    ||
1675           TerminalDevice->TerminalType == TerminalTypeTtyTerm   ||
1676           TerminalDevice->TerminalType == TerminalTypeLinux     ||
1677           TerminalDevice->TerminalType == TerminalTypeXtermR6   ||
1678           TerminalDevice->TerminalType == TerminalTypeVt400     ||
1679           TerminalDevice->TerminalType == TerminalTypeSCO) {
1680         switch (UnicodeChar) {
1681         case 'A':
1682           Key.ScanCode = SCAN_UP;
1683           break;
1684         case 'B':
1685           Key.ScanCode = SCAN_DOWN;
1686           break;
1687         case 'C':
1688           Key.ScanCode = SCAN_RIGHT;
1689           break;
1690         case 'D':
1691           Key.ScanCode = SCAN_LEFT;
1692           break;
1693         case 'H':
1694           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1695               TerminalDevice->TerminalType == TerminalTypeVt100  ||
1696               TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1697             Key.ScanCode = SCAN_HOME;
1698           }
1699           break;
1700         case 'F':
1701           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1702               TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1703             Key.ScanCode = SCAN_END;
1704           }
1705           break;
1706         case 'K':
1707           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1708             Key.ScanCode = SCAN_END;
1709           }
1710           break;
1711         case 'L':
1712         case '@':
1713           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1714               TerminalDevice->TerminalType == TerminalTypeVt100) {
1715             Key.ScanCode = SCAN_INSERT;
1716           }
1717           break;
1718         case 'X':
1719           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1720             Key.ScanCode = SCAN_DELETE;
1721           } else if (TerminalDevice->TerminalType == TerminalTypeSCO) {
1722             Key.ScanCode = SCAN_F12;
1723           }
1724           break;
1725         case 'P':
1726           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1727             Key.ScanCode = SCAN_DELETE;
1728           } else if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1729                       TerminalDevice->TerminalType == TerminalTypeSCO) {
1730             Key.ScanCode = SCAN_F4;
1731           }
1732           break;
1733         case 'I':
1734           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1735             Key.ScanCode = SCAN_PAGE_UP;
1736           }
1737           break;
1738         case 'V':
1739           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1740               TerminalDevice->TerminalType == TerminalTypeSCO) {
1741             Key.ScanCode = SCAN_F10;
1742           }
1743           break;
1744         case '?':
1745           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1746             Key.ScanCode = SCAN_PAGE_UP;
1747           }
1748           break;
1749         case 'G':
1750           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1751             Key.ScanCode = SCAN_PAGE_DOWN;
1752           }
1753           break;
1754         case 'U':
1755           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1756               TerminalDevice->TerminalType == TerminalTypeSCO) {
1757             Key.ScanCode = SCAN_F9;
1758           }
1759           break;
1760         case '/':
1761           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1762             Key.ScanCode = SCAN_PAGE_DOWN;
1763           }
1764           break;
1765         case 'M':
1766           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1767               TerminalDevice->TerminalType == TerminalTypeSCO) {
1768             Key.ScanCode = SCAN_F1;
1769           }
1770           break;
1771         case 'N':
1772           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1773               TerminalDevice->TerminalType == TerminalTypeSCO) {
1774             Key.ScanCode = SCAN_F2;
1775           }
1776           break;
1777         case 'O':
1778           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1779               TerminalDevice->TerminalType == TerminalTypeSCO) {
1780             Key.ScanCode = SCAN_F3;
1781           }
1782           break;
1783         case 'Q':
1784           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1785               TerminalDevice->TerminalType == TerminalTypeSCO) {
1786             Key.ScanCode = SCAN_F5;
1787           }
1788           break;
1789         case 'R':
1790           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1791               TerminalDevice->TerminalType == TerminalTypeSCO) {
1792             Key.ScanCode = SCAN_F6;
1793           }
1794           break;
1795         case 'S':
1796           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1797               TerminalDevice->TerminalType == TerminalTypeSCO) {
1798             Key.ScanCode = SCAN_F7;
1799           }
1800           break;
1801         case 'T':
1802           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1803               TerminalDevice->TerminalType == TerminalTypeSCO) {
1804             Key.ScanCode = SCAN_F8;
1805           }
1806           break;
1807         case 'W':
1808           if (TerminalDevice->TerminalType == TerminalTypeSCO) {
1809             Key.ScanCode = SCAN_F11;
1810           }
1811           break;
1812         default :
1813           break;
1814         }
1815       }
1816 
1817       /*
1818        * The VT220 escape codes that the TTY terminal accepts all have
1819        * numeric codes, and there are no ambiguous prefixes shared with
1820        * other terminal types.
1821        */
1822       if (TerminalDevice->TerminalType == TerminalTypeTtyTerm &&
1823           Key.ScanCode == SCAN_NULL &&
1824           UnicodeChar >= '0' &&
1825           UnicodeChar <= '9') {
1826         TerminalDevice->TtyEscapeStr[0] = UnicodeChar;
1827         TerminalDevice->TtyEscapeIndex = 1;
1828         TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET_TTY;
1829         continue;
1830       }
1831 
1832       if (Key.ScanCode != SCAN_NULL) {
1833         Key.UnicodeChar = 0;
1834         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1835         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1836         UnicodeToEfiKeyFlushState (TerminalDevice);
1837         continue;
1838       }
1839 
1840       UnicodeToEfiKeyFlushState (TerminalDevice);
1841 
1842       break;
1843 
1844     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_1:
1845 
1846       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1847 
1848       Key.ScanCode = SCAN_NULL;
1849 
1850       if (TerminalDevice->TerminalType == TerminalTypeXtermR6 ||
1851           TerminalDevice->TerminalType == TerminalTypeVt400 ||
1852           TerminalDevice->TerminalType == TerminalTypeLinux) {
1853         switch (UnicodeChar) {
1854         case '1':
1855           if (TerminalDevice->TerminalType == TerminalTypeVt400) {
1856             Key.ScanCode = SCAN_F1;
1857           }
1858           break;
1859         case '2':
1860           if (TerminalDevice->TerminalType == TerminalTypeVt400) {
1861             Key.ScanCode = SCAN_F2;
1862           }
1863           break;
1864         case '3':
1865           if (TerminalDevice->TerminalType == TerminalTypeVt400) {
1866             Key.ScanCode = SCAN_F3;
1867           }
1868           break;
1869         case '4':
1870           if (TerminalDevice->TerminalType == TerminalTypeVt400) {
1871             Key.ScanCode = SCAN_F4;
1872           }
1873           break;
1874         case '5':
1875           if (TerminalDevice->TerminalType == TerminalTypeXtermR6 ||
1876               TerminalDevice->TerminalType == TerminalTypeVt400) {
1877             Key.ScanCode = SCAN_F5;
1878           }
1879           break;
1880         case '7':
1881           Key.ScanCode = SCAN_F6;
1882           break;
1883         case '8':
1884           Key.ScanCode = SCAN_F7;
1885           break;
1886         case '9':
1887           Key.ScanCode = SCAN_F8;
1888           break;
1889         }
1890       }
1891 
1892       if (Key.ScanCode != SCAN_NULL) {
1893         Key.UnicodeChar = 0;
1894         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1895         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1896         UnicodeToEfiKeyFlushState (TerminalDevice);
1897         continue;
1898       }
1899 
1900       UnicodeToEfiKeyFlushState (TerminalDevice);
1901 
1902       break;
1903 
1904     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_2:
1905 
1906       TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1907       Key.ScanCode = SCAN_NULL;
1908       if (TerminalDevice->TerminalType == TerminalTypeXtermR6 ||
1909           TerminalDevice->TerminalType == TerminalTypeVt400 ||
1910           TerminalDevice->TerminalType == TerminalTypeLinux) {
1911         switch (UnicodeChar) {
1912         case '0':
1913           Key.ScanCode = SCAN_F9;
1914           break;
1915         case '1':
1916           Key.ScanCode = SCAN_F10;
1917           break;
1918         case '3':
1919           Key.ScanCode = SCAN_F11;
1920           break;
1921         case '4':
1922           Key.ScanCode = SCAN_F12;
1923           break;
1924         }
1925       }
1926 
1927       if (Key.ScanCode != SCAN_NULL) {
1928         Key.UnicodeChar = 0;
1929         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1930         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1931         UnicodeToEfiKeyFlushState (TerminalDevice);
1932         continue;
1933       }
1934 
1935       UnicodeToEfiKeyFlushState (TerminalDevice);
1936 
1937       break;
1938 
1939     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_LEFTOPENBRACKET_2ND:
1940 
1941       TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1942       Key.ScanCode = SCAN_NULL;
1943 
1944       if (TerminalDevice->TerminalType == TerminalTypeLinux) {
1945         switch (UnicodeChar) {
1946         case 'A':
1947           Key.ScanCode = SCAN_F1;
1948           break;
1949         case 'B':
1950           Key.ScanCode = SCAN_F2;
1951           break;
1952         case 'C':
1953           Key.ScanCode = SCAN_F3;
1954           break;
1955         case 'D':
1956           Key.ScanCode = SCAN_F4;
1957           break;
1958         case 'E':
1959           Key.ScanCode = SCAN_F5;
1960           break;
1961         }
1962       }
1963 
1964       if (Key.ScanCode != SCAN_NULL) {
1965         Key.UnicodeChar = 0;
1966         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1967         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1968         UnicodeToEfiKeyFlushState (TerminalDevice);
1969         continue;
1970       }
1971 
1972       UnicodeToEfiKeyFlushState (TerminalDevice);
1973 
1974       break;
1975 
1976     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_LEFTOPENBRACKET_TTY:
1977       /*
1978        * Here we handle the VT220 escape codes that we accept.  This
1979        * state is only used by the TTY terminal type.
1980        */
1981       Key.ScanCode = SCAN_NULL;
1982       if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1983 
1984         if (UnicodeChar == '~' && TerminalDevice->TtyEscapeIndex <= 2) {
1985           UINT16 EscCode;
1986           TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex] = 0; /* Terminate string */
1987           EscCode = (UINT16) StrDecimalToUintn(TerminalDevice->TtyEscapeStr);
1988           switch (EscCode) {
1989           case 2:
1990               Key.ScanCode = SCAN_INSERT;
1991               break;
1992           case 3:
1993               Key.ScanCode = SCAN_DELETE;
1994               break;
1995           case 5:
1996               Key.ScanCode = SCAN_PAGE_UP;
1997               break;
1998           case 6:
1999               Key.ScanCode = SCAN_PAGE_DOWN;
2000               break;
2001           case 11:
2002           case 12:
2003           case 13:
2004           case 14:
2005           case 15:
2006             Key.ScanCode = SCAN_F1 + EscCode - 11;
2007             break;
2008           case 17:
2009           case 18:
2010           case 19:
2011           case 20:
2012           case 21:
2013             Key.ScanCode = SCAN_F6 + EscCode - 17;
2014             break;
2015           case 23:
2016           case 24:
2017             Key.ScanCode = SCAN_F11 + EscCode - 23;
2018             break;
2019           default:
2020             break;
2021           }
2022         } else if (TerminalDevice->TtyEscapeIndex == 1){
2023           /* 2 character escape code   */
2024           TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex++] = UnicodeChar;
2025           continue;
2026         }
2027         else {
2028           DEBUG ((EFI_D_ERROR, "Unexpected state in escape2\n"));
2029         }
2030       }
2031       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
2032 
2033       if (Key.ScanCode != SCAN_NULL) {
2034         Key.UnicodeChar = 0;
2035         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
2036         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
2037         UnicodeToEfiKeyFlushState (TerminalDevice);
2038         continue;
2039       }
2040 
2041       UnicodeToEfiKeyFlushState (TerminalDevice);
2042       break;
2043 
2044     default:
2045       //
2046       // Invalid state. This should never happen.
2047       //
2048       ASSERT (FALSE);
2049 
2050       UnicodeToEfiKeyFlushState (TerminalDevice);
2051 
2052       break;
2053     }
2054 
2055     if (UnicodeChar == ESC) {
2056       TerminalDevice->InputState = INPUT_STATE_ESC;
2057     }
2058 
2059     if (UnicodeChar == CSI) {
2060       TerminalDevice->InputState = INPUT_STATE_CSI;
2061     }
2062 
2063     if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) {
2064       Status = gBS->SetTimer(
2065                       TerminalDevice->TwoSecondTimeOut,
2066                       TimerRelative,
2067                       (UINT64)20000000
2068                       );
2069       ASSERT_EFI_ERROR (Status);
2070       continue;
2071     }
2072 
2073     if (SetDefaultResetState) {
2074       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
2075     }
2076 
2077     if (UnicodeChar == DEL) {
2078       if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
2079         Key.ScanCode    = SCAN_NULL;
2080         Key.UnicodeChar = CHAR_BACKSPACE;
2081       }
2082       else {
2083         Key.ScanCode    = SCAN_DELETE;
2084         Key.UnicodeChar = 0;
2085       }
2086     } else {
2087       Key.ScanCode    = SCAN_NULL;
2088       Key.UnicodeChar = UnicodeChar;
2089     }
2090 
2091     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
2092   }
2093 }
2094