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 - 2018, 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     AnsiRawDataToUnicode (TerminalDevice);
457     UnicodeToEfiKey (TerminalDevice);
458     break;
459 
460   case TerminalTypeVtUtf8:
461     //
462     // Process all the raw data in the RawFIFO,
463     // put the processed key into UnicodeFIFO.
464     //
465     VTUTF8RawDataToUnicode (TerminalDevice);
466 
467     //
468     // Translate all the Unicode data in the UnicodeFIFO to Efi key,
469     // then put into EfiKeyFIFO.
470     //
471     UnicodeToEfiKey (TerminalDevice);
472 
473     break;
474   }
475 }
476 
477 /**
478   Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event
479   Signal the event if there is key available
480 
481   @param  Event                    Indicates the event that invoke this function.
482   @param  Context                  Indicates the calling context.
483 
484 **/
485 VOID
486 EFIAPI
TerminalConInWaitForKey(IN EFI_EVENT Event,IN VOID * Context)487 TerminalConInWaitForKey (
488   IN  EFI_EVENT       Event,
489   IN  VOID            *Context
490   )
491 {
492   //
493   // Someone is waiting on the keystroke event, if there's
494   // a key pending, signal the event
495   //
496   if (!IsEfiKeyFiFoEmpty ((TERMINAL_DEV *) Context)) {
497 
498     gBS->SignalEvent (Event);
499   }
500 }
501 
502 /**
503   Timer handler to poll the key from serial.
504 
505   @param  Event                    Indicates the event that invoke this function.
506   @param  Context                  Indicates the calling context.
507 **/
508 VOID
509 EFIAPI
TerminalConInTimerHandler(IN EFI_EVENT Event,IN VOID * Context)510 TerminalConInTimerHandler (
511   IN EFI_EVENT            Event,
512   IN VOID                 *Context
513   )
514 {
515   EFI_STATUS              Status;
516   TERMINAL_DEV            *TerminalDevice;
517   UINT32                  Control;
518   UINT8                   Input;
519   EFI_SERIAL_IO_MODE      *Mode;
520   EFI_SERIAL_IO_PROTOCOL  *SerialIo;
521   UINTN                   SerialInTimeOut;
522 
523   TerminalDevice  = (TERMINAL_DEV *) Context;
524 
525   SerialIo        = TerminalDevice->SerialIo;
526   if (SerialIo == NULL) {
527     return ;
528   }
529   //
530   //  if current timeout value for serial device is not identical with
531   //  the value saved in TERMINAL_DEV structure, then recalculate the
532   //  timeout value again and set serial attribute according to this value.
533   //
534   Mode = SerialIo->Mode;
535   if (Mode->Timeout != TerminalDevice->SerialInTimeOut) {
536 
537     SerialInTimeOut = 0;
538     if (Mode->BaudRate != 0) {
539       //
540       // According to BAUD rate to calculate the timeout value.
541       //
542       SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate;
543     }
544 
545     Status = SerialIo->SetAttributes (
546                         SerialIo,
547                         Mode->BaudRate,
548                         Mode->ReceiveFifoDepth,
549                         (UINT32) SerialInTimeOut,
550                         (EFI_PARITY_TYPE) (Mode->Parity),
551                         (UINT8) Mode->DataBits,
552                         (EFI_STOP_BITS_TYPE) (Mode->StopBits)
553                         );
554 
555     if (EFI_ERROR (Status)) {
556       TerminalDevice->SerialInTimeOut = 0;
557     } else {
558       TerminalDevice->SerialInTimeOut = SerialInTimeOut;
559     }
560   }
561   //
562   // Check whether serial buffer is empty.
563   // Skip the key transfer loop only if the SerialIo protocol instance
564   // successfully reports EFI_SERIAL_INPUT_BUFFER_EMPTY.
565   //
566   Status = SerialIo->GetControl (SerialIo, &Control);
567   if (EFI_ERROR (Status) || ((Control & EFI_SERIAL_INPUT_BUFFER_EMPTY) == 0)) {
568     //
569     // Fetch all the keys in the serial buffer,
570     // and insert the byte stream into RawFIFO.
571     //
572     while (!IsRawFiFoFull (TerminalDevice)) {
573 
574       Status = GetOneKeyFromSerial (TerminalDevice->SerialIo, &Input);
575 
576       if (EFI_ERROR (Status)) {
577         if (Status == EFI_DEVICE_ERROR) {
578           REPORT_STATUS_CODE_WITH_DEVICE_PATH (
579             EFI_ERROR_CODE | EFI_ERROR_MINOR,
580             (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_INPUT_ERROR),
581             TerminalDevice->DevicePath
582             );
583         }
584         break;
585       }
586 
587       RawFiFoInsertOneKey (TerminalDevice, Input);
588     }
589   }
590 
591   //
592   // Translate all the raw data in RawFIFO into EFI Key,
593   // according to different terminal type supported.
594   //
595   TranslateRawDataToEfiKey (TerminalDevice);
596 }
597 
598 /**
599   Process key notify.
600 
601   @param  Event                 Indicates the event that invoke this function.
602   @param  Context               Indicates the calling context.
603 **/
604 VOID
605 EFIAPI
KeyNotifyProcessHandler(IN EFI_EVENT Event,IN VOID * Context)606 KeyNotifyProcessHandler (
607   IN  EFI_EVENT                 Event,
608   IN  VOID                      *Context
609   )
610 {
611   BOOLEAN                       HasKey;
612   TERMINAL_DEV                  *TerminalDevice;
613   EFI_INPUT_KEY                 Key;
614   EFI_KEY_DATA                  KeyData;
615   LIST_ENTRY                    *Link;
616   LIST_ENTRY                    *NotifyList;
617   TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
618   EFI_TPL                       OldTpl;
619 
620   TerminalDevice = (TERMINAL_DEV *) Context;
621 
622   //
623   // Invoke notification functions.
624   //
625   NotifyList = &TerminalDevice->NotifyList;
626   while (TRUE) {
627     //
628     // Enter critical section
629     //
630     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
631     HasKey = EfiKeyFiFoForNotifyRemoveOneKey (TerminalDevice->EfiKeyFiFoForNotify, &Key);
632     CopyMem (&KeyData.Key, &Key, sizeof (EFI_INPUT_KEY));
633     KeyData.KeyState.KeyShiftState  = 0;
634     KeyData.KeyState.KeyToggleState = 0;
635     //
636     // Leave critical section
637     //
638     gBS->RestoreTPL (OldTpl);
639     if (!HasKey) {
640       break;
641     }
642     for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) {
643       CurrentNotify = CR (Link, TERMINAL_CONSOLE_IN_EX_NOTIFY, NotifyEntry, TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE);
644       if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
645         CurrentNotify->KeyNotificationFn (&KeyData);
646       }
647     }
648   }
649 }
650 
651 /**
652   Get one key out of serial buffer.
653 
654   @param  SerialIo           Serial I/O protocol attached to the serial device.
655   @param  Output             The fetched key.
656 
657   @retval EFI_NOT_READY      If serial buffer is empty.
658   @retval EFI_DEVICE_ERROR   If reading serial buffer encounter error.
659   @retval EFI_SUCCESS        If reading serial buffer successfully, put
660                              the fetched key to the parameter output.
661 
662 **/
663 EFI_STATUS
GetOneKeyFromSerial(EFI_SERIAL_IO_PROTOCOL * SerialIo,UINT8 * Output)664 GetOneKeyFromSerial (
665   EFI_SERIAL_IO_PROTOCOL  *SerialIo,
666   UINT8                   *Output
667   )
668 {
669   EFI_STATUS  Status;
670   UINTN       Size;
671 
672   Size    = 1;
673   *Output = 0;
674 
675   //
676   // Read one key from serial I/O device.
677   //
678   Status  = SerialIo->Read (SerialIo, &Size, Output);
679 
680   if (EFI_ERROR (Status)) {
681 
682     if (Status == EFI_TIMEOUT) {
683       return EFI_NOT_READY;
684     }
685 
686     return EFI_DEVICE_ERROR;
687 
688   }
689 
690   if (*Output == 0) {
691     return EFI_NOT_READY;
692   }
693 
694   return EFI_SUCCESS;
695 }
696 
697 /**
698   Insert one byte raw data into the Raw Data FIFO.
699 
700   @param  TerminalDevice       Terminal driver private structure.
701   @param  Input                The key will be input.
702 
703   @retval TRUE                 If insert successfully.
704   @retval FALSE                If Raw Data buffer is full before key insertion,
705                                and the key is lost.
706 
707 **/
708 BOOLEAN
RawFiFoInsertOneKey(TERMINAL_DEV * TerminalDevice,UINT8 Input)709 RawFiFoInsertOneKey (
710   TERMINAL_DEV      *TerminalDevice,
711   UINT8             Input
712   )
713 {
714   UINT8 Tail;
715 
716   Tail = TerminalDevice->RawFiFo->Tail;
717 
718   if (IsRawFiFoFull (TerminalDevice)) {
719     //
720     // Raw FIFO is full
721     //
722     return FALSE;
723   }
724 
725   TerminalDevice->RawFiFo->Data[Tail]  = Input;
726 
727   TerminalDevice->RawFiFo->Tail        = (UINT8) ((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1));
728 
729   return TRUE;
730 }
731 
732 /**
733   Remove one pre-fetched key out of the Raw Data FIFO.
734 
735   @param  TerminalDevice       Terminal driver private structure.
736   @param  Output               The key will be removed.
737 
738   @retval TRUE                 If insert successfully.
739   @retval FALSE                If Raw Data FIFO buffer is empty before remove operation.
740 
741 **/
742 BOOLEAN
RawFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,UINT8 * Output)743 RawFiFoRemoveOneKey (
744   TERMINAL_DEV  *TerminalDevice,
745   UINT8         *Output
746   )
747 {
748   UINT8 Head;
749 
750   Head = TerminalDevice->RawFiFo->Head;
751 
752   if (IsRawFiFoEmpty (TerminalDevice)) {
753     //
754     //  FIFO is empty
755     //
756     *Output = 0;
757     return FALSE;
758   }
759 
760   *Output                       = TerminalDevice->RawFiFo->Data[Head];
761 
762   TerminalDevice->RawFiFo->Head  = (UINT8) ((Head + 1) % (RAW_FIFO_MAX_NUMBER + 1));
763 
764   return TRUE;
765 }
766 
767 /**
768   Clarify whether Raw Data FIFO buffer is empty.
769 
770   @param  TerminalDevice       Terminal driver private structure
771 
772   @retval TRUE                 If Raw Data FIFO buffer is empty.
773   @retval FALSE                If Raw Data FIFO buffer is not empty.
774 
775 **/
776 BOOLEAN
IsRawFiFoEmpty(TERMINAL_DEV * TerminalDevice)777 IsRawFiFoEmpty (
778   TERMINAL_DEV  *TerminalDevice
779   )
780 {
781   if (TerminalDevice->RawFiFo->Head == TerminalDevice->RawFiFo->Tail) {
782     return TRUE;
783   } else {
784     return FALSE;
785   }
786 }
787 
788 /**
789   Clarify whether Raw Data FIFO buffer is full.
790 
791   @param  TerminalDevice       Terminal driver private structure
792 
793   @retval TRUE                 If Raw Data FIFO buffer is full.
794   @retval FALSE                If Raw Data FIFO buffer is not full.
795 
796 **/
797 BOOLEAN
IsRawFiFoFull(TERMINAL_DEV * TerminalDevice)798 IsRawFiFoFull (
799   TERMINAL_DEV  *TerminalDevice
800   )
801 {
802   UINT8 Tail;
803   UINT8 Head;
804 
805   Tail  = TerminalDevice->RawFiFo->Tail;
806   Head  = TerminalDevice->RawFiFo->Head;
807 
808   if (((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1)) == Head) {
809 
810     return TRUE;
811   }
812 
813   return FALSE;
814 }
815 
816 /**
817   Insert one pre-fetched key into the FIFO buffer.
818 
819   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
820   @param  Input                 The key will be input.
821 
822   @retval TRUE                  If insert successfully.
823   @retval FALSE                 If FIFO buffer is full before key insertion,
824                                 and the key is lost.
825 
826 **/
827 BOOLEAN
EfiKeyFiFoForNotifyInsertOneKey(EFI_KEY_FIFO * EfiKeyFiFo,EFI_INPUT_KEY * Input)828 EfiKeyFiFoForNotifyInsertOneKey (
829   EFI_KEY_FIFO                  *EfiKeyFiFo,
830   EFI_INPUT_KEY                 *Input
831   )
832 {
833   UINT8                         Tail;
834 
835   Tail = EfiKeyFiFo->Tail;
836 
837   if (IsEfiKeyFiFoForNotifyFull (EfiKeyFiFo)) {
838     //
839     // FIFO is full
840     //
841     return FALSE;
842   }
843 
844   CopyMem (&EfiKeyFiFo->Data[Tail], Input, sizeof (EFI_INPUT_KEY));
845 
846   EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
847 
848   return TRUE;
849 }
850 
851 /**
852   Remove one pre-fetched key out of the FIFO buffer.
853 
854   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
855   @param  Output                The key will be removed.
856 
857   @retval TRUE                  If remove successfully.
858   @retval FALSE                 If FIFO buffer is empty before remove operation.
859 
860 **/
861 BOOLEAN
EfiKeyFiFoForNotifyRemoveOneKey(EFI_KEY_FIFO * EfiKeyFiFo,EFI_INPUT_KEY * Output)862 EfiKeyFiFoForNotifyRemoveOneKey (
863   EFI_KEY_FIFO                  *EfiKeyFiFo,
864   EFI_INPUT_KEY                 *Output
865   )
866 {
867   UINT8                         Head;
868 
869   Head = EfiKeyFiFo->Head;
870   ASSERT (Head < FIFO_MAX_NUMBER + 1);
871 
872   if (IsEfiKeyFiFoForNotifyEmpty (EfiKeyFiFo)) {
873     //
874     // FIFO is empty
875     //
876     Output->ScanCode    = SCAN_NULL;
877     Output->UnicodeChar = 0;
878     return FALSE;
879   }
880 
881   CopyMem (Output, &EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY));
882 
883   EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
884 
885   return TRUE;
886 }
887 
888 /**
889   Clarify whether FIFO buffer is empty.
890 
891   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
892 
893   @retval TRUE                  If FIFO buffer is empty.
894   @retval FALSE                 If FIFO buffer is not empty.
895 
896 **/
897 BOOLEAN
IsEfiKeyFiFoForNotifyEmpty(EFI_KEY_FIFO * EfiKeyFiFo)898 IsEfiKeyFiFoForNotifyEmpty (
899   EFI_KEY_FIFO                  *EfiKeyFiFo
900   )
901 {
902   if (EfiKeyFiFo->Head == EfiKeyFiFo->Tail) {
903     return TRUE;
904   } else {
905     return FALSE;
906   }
907 }
908 
909 /**
910   Clarify whether FIFO buffer is full.
911 
912   @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.
913 
914   @retval TRUE                  If FIFO buffer is full.
915   @retval FALSE                 If FIFO buffer is not full.
916 
917 **/
918 BOOLEAN
IsEfiKeyFiFoForNotifyFull(EFI_KEY_FIFO * EfiKeyFiFo)919 IsEfiKeyFiFoForNotifyFull (
920   EFI_KEY_FIFO                  *EfiKeyFiFo
921   )
922 {
923   UINT8                         Tail;
924   UINT8                         Head;
925 
926   Tail = EfiKeyFiFo->Tail;
927   Head = EfiKeyFiFo->Head;
928 
929   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
930     return TRUE;
931   }
932 
933   return FALSE;
934 }
935 
936 /**
937   Insert one pre-fetched key into the FIFO buffer.
938 
939   @param  TerminalDevice       Terminal driver private structure.
940   @param  Key                  The key will be input.
941 
942   @retval TRUE                 If insert successfully.
943   @retval FALSE                If FIFO buffer is full before key insertion,
944                                and the key is lost.
945 
946 **/
947 BOOLEAN
EfiKeyFiFoInsertOneKey(TERMINAL_DEV * TerminalDevice,EFI_INPUT_KEY * Key)948 EfiKeyFiFoInsertOneKey (
949   TERMINAL_DEV                    *TerminalDevice,
950   EFI_INPUT_KEY                   *Key
951   )
952 {
953   UINT8                           Tail;
954   LIST_ENTRY                      *Link;
955   LIST_ENTRY                      *NotifyList;
956   TERMINAL_CONSOLE_IN_EX_NOTIFY   *CurrentNotify;
957   EFI_KEY_DATA                    KeyData;
958 
959   Tail = TerminalDevice->EfiKeyFiFo->Tail;
960 
961   CopyMem (&KeyData.Key, Key, sizeof (EFI_INPUT_KEY));
962   KeyData.KeyState.KeyShiftState  = 0;
963   KeyData.KeyState.KeyToggleState = 0;
964 
965   //
966   // Signal KeyNotify process event if this key pressed matches any key registered.
967   //
968   NotifyList = &TerminalDevice->NotifyList;
969   for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) {
970     CurrentNotify = CR (
971                       Link,
972                       TERMINAL_CONSOLE_IN_EX_NOTIFY,
973                       NotifyEntry,
974                       TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
975                       );
976     if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
977       //
978       // The key notification function needs to run at TPL_CALLBACK
979       // while current TPL is TPL_NOTIFY. It will be invoked in
980       // KeyNotifyProcessHandler() which runs at TPL_CALLBACK.
981       //
982       EfiKeyFiFoForNotifyInsertOneKey (TerminalDevice->EfiKeyFiFoForNotify, Key);
983       gBS->SignalEvent (TerminalDevice->KeyNotifyProcessEvent);
984       break;
985     }
986   }
987   if (IsEfiKeyFiFoFull (TerminalDevice)) {
988     //
989     // Efi Key FIFO is full
990     //
991     return FALSE;
992   }
993 
994   CopyMem (&TerminalDevice->EfiKeyFiFo->Data[Tail], Key, sizeof (EFI_INPUT_KEY));
995 
996   TerminalDevice->EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
997 
998   return TRUE;
999 }
1000 
1001 /**
1002   Remove one pre-fetched key out of the FIFO buffer.
1003 
1004   @param  TerminalDevice       Terminal driver private structure.
1005   @param  Output               The key will be removed.
1006 
1007   @retval TRUE                 If insert successfully.
1008   @retval FALSE                If FIFO buffer is empty before remove operation.
1009 
1010 **/
1011 BOOLEAN
EfiKeyFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,EFI_INPUT_KEY * Output)1012 EfiKeyFiFoRemoveOneKey (
1013   TERMINAL_DEV  *TerminalDevice,
1014   EFI_INPUT_KEY *Output
1015   )
1016 {
1017   UINT8 Head;
1018 
1019   Head = TerminalDevice->EfiKeyFiFo->Head;
1020   ASSERT (Head < FIFO_MAX_NUMBER + 1);
1021 
1022   if (IsEfiKeyFiFoEmpty (TerminalDevice)) {
1023     //
1024     //  FIFO is empty
1025     //
1026     Output->ScanCode    = SCAN_NULL;
1027     Output->UnicodeChar = 0;
1028     return FALSE;
1029   }
1030 
1031   CopyMem (Output, &TerminalDevice->EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY));
1032 
1033   TerminalDevice->EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
1034 
1035   return TRUE;
1036 }
1037 
1038 /**
1039   Clarify whether FIFO buffer is empty.
1040 
1041   @param  TerminalDevice       Terminal driver private structure
1042 
1043   @retval TRUE                 If FIFO buffer is empty.
1044   @retval FALSE                If FIFO buffer is not empty.
1045 
1046 **/
1047 BOOLEAN
IsEfiKeyFiFoEmpty(TERMINAL_DEV * TerminalDevice)1048 IsEfiKeyFiFoEmpty (
1049   TERMINAL_DEV  *TerminalDevice
1050   )
1051 {
1052   if (TerminalDevice->EfiKeyFiFo->Head == TerminalDevice->EfiKeyFiFo->Tail) {
1053     return TRUE;
1054   } else {
1055     return FALSE;
1056   }
1057 }
1058 
1059 /**
1060   Clarify whether FIFO buffer is full.
1061 
1062   @param  TerminalDevice       Terminal driver private structure
1063 
1064   @retval TRUE                 If FIFO buffer is full.
1065   @retval FALSE                If FIFO buffer is not full.
1066 
1067 **/
1068 BOOLEAN
IsEfiKeyFiFoFull(TERMINAL_DEV * TerminalDevice)1069 IsEfiKeyFiFoFull (
1070   TERMINAL_DEV  *TerminalDevice
1071   )
1072 {
1073   UINT8 Tail;
1074   UINT8 Head;
1075 
1076   Tail  = TerminalDevice->EfiKeyFiFo->Tail;
1077   Head  = TerminalDevice->EfiKeyFiFo->Head;
1078 
1079   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
1080 
1081     return TRUE;
1082   }
1083 
1084   return FALSE;
1085 }
1086 
1087 /**
1088   Insert one pre-fetched key into the Unicode FIFO buffer.
1089 
1090   @param  TerminalDevice       Terminal driver private structure.
1091   @param  Input                The key will be input.
1092 
1093   @retval TRUE                 If insert successfully.
1094   @retval FALSE                If Unicode FIFO buffer is full before key insertion,
1095                                and the key is lost.
1096 
1097 **/
1098 BOOLEAN
UnicodeFiFoInsertOneKey(TERMINAL_DEV * TerminalDevice,UINT16 Input)1099 UnicodeFiFoInsertOneKey (
1100   TERMINAL_DEV      *TerminalDevice,
1101   UINT16            Input
1102   )
1103 {
1104   UINT8 Tail;
1105 
1106   Tail = TerminalDevice->UnicodeFiFo->Tail;
1107   ASSERT (Tail < FIFO_MAX_NUMBER + 1);
1108 
1109 
1110   if (IsUnicodeFiFoFull (TerminalDevice)) {
1111     //
1112     // Unicode FIFO is full
1113     //
1114     return FALSE;
1115   }
1116 
1117   TerminalDevice->UnicodeFiFo->Data[Tail]  = Input;
1118 
1119   TerminalDevice->UnicodeFiFo->Tail        = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));
1120 
1121   return TRUE;
1122 }
1123 
1124 /**
1125   Remove one pre-fetched key out of the Unicode FIFO buffer.
1126   The caller should guarantee that Unicode FIFO buffer is not empty
1127   by IsUnicodeFiFoEmpty ().
1128 
1129   @param  TerminalDevice       Terminal driver private structure.
1130   @param  Output               The key will be removed.
1131 
1132 **/
1133 VOID
UnicodeFiFoRemoveOneKey(TERMINAL_DEV * TerminalDevice,UINT16 * Output)1134 UnicodeFiFoRemoveOneKey (
1135   TERMINAL_DEV  *TerminalDevice,
1136   UINT16        *Output
1137   )
1138 {
1139   UINT8 Head;
1140 
1141   Head = TerminalDevice->UnicodeFiFo->Head;
1142   ASSERT (Head < FIFO_MAX_NUMBER + 1);
1143 
1144   *Output = TerminalDevice->UnicodeFiFo->Data[Head];
1145 
1146   TerminalDevice->UnicodeFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));
1147 }
1148 
1149 /**
1150   Clarify whether Unicode FIFO buffer is empty.
1151 
1152   @param  TerminalDevice       Terminal driver private structure
1153 
1154   @retval TRUE                 If Unicode FIFO buffer is empty.
1155   @retval FALSE                If Unicode FIFO buffer is not empty.
1156 
1157 **/
1158 BOOLEAN
IsUnicodeFiFoEmpty(TERMINAL_DEV * TerminalDevice)1159 IsUnicodeFiFoEmpty (
1160   TERMINAL_DEV  *TerminalDevice
1161   )
1162 {
1163   if (TerminalDevice->UnicodeFiFo->Head == TerminalDevice->UnicodeFiFo->Tail) {
1164     return TRUE;
1165   } else {
1166     return FALSE;
1167   }
1168 }
1169 
1170 /**
1171   Clarify whether Unicode FIFO buffer is full.
1172 
1173   @param  TerminalDevice       Terminal driver private structure
1174 
1175   @retval TRUE                 If Unicode FIFO buffer is full.
1176   @retval FALSE                If Unicode FIFO buffer is not full.
1177 
1178 **/
1179 BOOLEAN
IsUnicodeFiFoFull(TERMINAL_DEV * TerminalDevice)1180 IsUnicodeFiFoFull (
1181   TERMINAL_DEV  *TerminalDevice
1182   )
1183 {
1184   UINT8 Tail;
1185   UINT8 Head;
1186 
1187   Tail  = TerminalDevice->UnicodeFiFo->Tail;
1188   Head  = TerminalDevice->UnicodeFiFo->Head;
1189 
1190   if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {
1191 
1192     return TRUE;
1193   }
1194 
1195   return FALSE;
1196 }
1197 
1198 
1199 /**
1200   Update the Unicode characters from a terminal input device into EFI Keys FIFO.
1201 
1202   @param TerminalDevice   The terminal device to use to translate raw input into EFI Keys
1203 
1204 **/
1205 VOID
UnicodeToEfiKeyFlushState(IN TERMINAL_DEV * TerminalDevice)1206 UnicodeToEfiKeyFlushState (
1207   IN  TERMINAL_DEV    *TerminalDevice
1208   )
1209 {
1210   EFI_INPUT_KEY Key;
1211   UINT32        InputState;
1212 
1213   InputState = TerminalDevice->InputState;
1214 
1215   if (IsEfiKeyFiFoFull (TerminalDevice)) {
1216     return;
1217   }
1218 
1219   if ((InputState & INPUT_STATE_ESC) != 0) {
1220     Key.ScanCode    = SCAN_ESC;
1221     Key.UnicodeChar = 0;
1222     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1223   }
1224 
1225   if ((InputState & INPUT_STATE_CSI) != 0) {
1226     Key.ScanCode    = SCAN_NULL;
1227     Key.UnicodeChar = CSI;
1228     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1229   }
1230 
1231   if ((InputState & INPUT_STATE_LEFTOPENBRACKET) != 0) {
1232     Key.ScanCode    = SCAN_NULL;
1233     Key.UnicodeChar = LEFTOPENBRACKET;
1234     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1235   }
1236 
1237   if ((InputState & INPUT_STATE_O) != 0) {
1238     Key.ScanCode    = SCAN_NULL;
1239     Key.UnicodeChar = 'O';
1240     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1241   }
1242 
1243   if ((InputState & INPUT_STATE_2) != 0) {
1244     Key.ScanCode    = SCAN_NULL;
1245     Key.UnicodeChar = '2';
1246     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1247   }
1248 
1249   //
1250   // Cancel the timer.
1251   //
1252   gBS->SetTimer (
1253         TerminalDevice->TwoSecondTimeOut,
1254         TimerCancel,
1255         0
1256         );
1257 
1258   TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1259 }
1260 
1261 
1262 /**
1263   Converts a stream of Unicode characters from a terminal input device into EFI Keys that
1264   can be read through the Simple Input Protocol.
1265 
1266   The table below shows the keyboard input mappings that this function supports.
1267   If the ESC sequence listed in one of the columns is presented, then it is translated
1268   into the corresponding EFI Scan Code.  If a matching sequence is not found, then the raw
1269   key strokes are converted into EFI Keys.
1270 
1271   2 seconds are allowed for an ESC sequence to be completed.  If the ESC sequence is not
1272   completed in 2 seconds, then the raw key strokes of the partial ESC sequence are
1273   converted into EFI Keys.
1274   There is one special input sequence that will force the system to reset.
1275   This is ESC R ESC r ESC R.
1276 
1277   Note: current implementation support terminal types include: PC ANSI, VT100+/VTUTF8, VT100.
1278         The table below is not same with UEFI Spec 2.3 Appendix B Table 201(not support ANSI X3.64 /
1279         DEC VT200-500 and extra support PC ANSI, VT100)since UEFI Table 201 is just an example.
1280 
1281   Symbols used in table below
1282   ===========================
1283     ESC = 0x1B
1284     CSI = 0x9B
1285     DEL = 0x7f
1286     ^   = CTRL
1287 
1288   +=========+======+===========+==========+==========+
1289   |         | EFI  | UEFI 2.0  |          |          |
1290   |         | Scan |           |  VT100+  |          |
1291   |   KEY   | Code |  PC ANSI  |  VTUTF8  |   VT100  |
1292   +=========+======+===========+==========+==========+
1293   | NULL    | 0x00 |           |          |          |
1294   | UP      | 0x01 | ESC [ A   | ESC [ A  | ESC [ A  |
1295   | DOWN    | 0x02 | ESC [ B   | ESC [ B  | ESC [ B  |
1296   | RIGHT   | 0x03 | ESC [ C   | ESC [ C  | ESC [ C  |
1297   | LEFT    | 0x04 | ESC [ D   | ESC [ D  | ESC [ D  |
1298   | HOME    | 0x05 | ESC [ H   | ESC h    | ESC [ H  |
1299   | END     | 0x06 | ESC [ F   | ESC k    | ESC [ K  |
1300   | INSERT  | 0x07 | ESC [ @   | ESC +    | ESC [ @  |
1301   |         |      | ESC [ L   |          | ESC [ L  |
1302   | DELETE  | 0x08 | ESC [ X   | ESC -    | ESC [ P  |
1303   | PG UP   | 0x09 | ESC [ I   | ESC ?    | ESC [ V  |
1304   |         |      |           |          | ESC [ ?  |
1305   | PG DOWN | 0x0A | ESC [ G   | ESC /    | ESC [ U  |
1306   |         |      |           |          | ESC [ /  |
1307   | F1      | 0x0B | ESC [ M   | ESC 1    | ESC O P  |
1308   | F2      | 0x0C | ESC [ N   | ESC 2    | ESC O Q  |
1309   | F3      | 0x0D | ESC [ O   | ESC 3    | ESC O w  |
1310   | F4      | 0x0E | ESC [ P   | ESC 4    | ESC O x  |
1311   | F5      | 0x0F | ESC [ Q   | ESC 5    | ESC O t  |
1312   | F6      | 0x10 | ESC [ R   | ESC 6    | ESC O u  |
1313   | F7      | 0x11 | ESC [ S   | ESC 7    | ESC O q  |
1314   | F8      | 0x12 | ESC [ T   | ESC 8    | ESC O r  |
1315   | F9      | 0x13 | ESC [ U   | ESC 9    | ESC O p  |
1316   | F10     | 0x14 | ESC [ V   | ESC 0    | ESC O M  |
1317   | Escape  | 0x17 | ESC       | ESC      | ESC      |
1318   | F11     | 0x15 |           | ESC !    |          |
1319   | F12     | 0x16 |           | ESC @    |          |
1320   +=========+======+===========+==========+==========+
1321 
1322   Special Mappings
1323   ================
1324   ESC R ESC r ESC R = Reset System
1325 
1326   @param TerminalDevice   The terminal device to use to translate raw input into EFI Keys
1327 
1328 **/
1329 VOID
UnicodeToEfiKey(IN TERMINAL_DEV * TerminalDevice)1330 UnicodeToEfiKey (
1331   IN  TERMINAL_DEV    *TerminalDevice
1332   )
1333 {
1334   EFI_STATUS          Status;
1335   EFI_STATUS          TimerStatus;
1336   UINT16              UnicodeChar;
1337   EFI_INPUT_KEY       Key;
1338   BOOLEAN             SetDefaultResetState;
1339 
1340   TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut);
1341 
1342   if (!EFI_ERROR (TimerStatus)) {
1343     UnicodeToEfiKeyFlushState (TerminalDevice);
1344     TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1345   }
1346 
1347   while (!IsUnicodeFiFoEmpty (TerminalDevice) && !IsEfiKeyFiFoFull (TerminalDevice)) {
1348 
1349     if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) {
1350       //
1351       // Check to see if the 2 seconds timer has expired
1352       //
1353       TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut);
1354       if (!EFI_ERROR (TimerStatus)) {
1355         UnicodeToEfiKeyFlushState (TerminalDevice);
1356         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1357       }
1358     }
1359 
1360     //
1361     // Fetch one Unicode character from the Unicode FIFO
1362     //
1363     UnicodeFiFoRemoveOneKey (TerminalDevice, &UnicodeChar);
1364 
1365     SetDefaultResetState = TRUE;
1366 
1367     switch (TerminalDevice->InputState) {
1368     case INPUT_STATE_DEFAULT:
1369 
1370       break;
1371 
1372     case INPUT_STATE_ESC:
1373 
1374       if (UnicodeChar == LEFTOPENBRACKET) {
1375         TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET;
1376         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1377         continue;
1378       }
1379 
1380       if (UnicodeChar == 'O' && (TerminalDevice->TerminalType == TerminalTypeVt100 ||
1381                                  TerminalDevice->TerminalType == TerminalTypeTtyTerm)) {
1382         TerminalDevice->InputState |= INPUT_STATE_O;
1383         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1384         continue;
1385       }
1386 
1387       Key.ScanCode = SCAN_NULL;
1388 
1389       if (TerminalDevice->TerminalType == TerminalTypeVt100Plus ||
1390           TerminalDevice->TerminalType == TerminalTypeVtUtf8) {
1391         switch (UnicodeChar) {
1392         case '1':
1393           Key.ScanCode = SCAN_F1;
1394           break;
1395         case '2':
1396           Key.ScanCode = SCAN_F2;
1397           break;
1398         case '3':
1399           Key.ScanCode = SCAN_F3;
1400           break;
1401         case '4':
1402           Key.ScanCode = SCAN_F4;
1403           break;
1404         case '5':
1405           Key.ScanCode = SCAN_F5;
1406           break;
1407         case '6':
1408           Key.ScanCode = SCAN_F6;
1409           break;
1410         case '7':
1411           Key.ScanCode = SCAN_F7;
1412           break;
1413         case '8':
1414           Key.ScanCode = SCAN_F8;
1415           break;
1416         case '9':
1417           Key.ScanCode = SCAN_F9;
1418           break;
1419         case '0':
1420           Key.ScanCode = SCAN_F10;
1421           break;
1422         case '!':
1423           Key.ScanCode = SCAN_F11;
1424           break;
1425         case '@':
1426           Key.ScanCode = SCAN_F12;
1427           break;
1428         case 'h':
1429           Key.ScanCode = SCAN_HOME;
1430           break;
1431         case 'k':
1432           Key.ScanCode = SCAN_END;
1433           break;
1434         case '+':
1435           Key.ScanCode = SCAN_INSERT;
1436           break;
1437         case '-':
1438           Key.ScanCode = SCAN_DELETE;
1439           break;
1440         case '/':
1441           Key.ScanCode = SCAN_PAGE_DOWN;
1442           break;
1443         case '?':
1444           Key.ScanCode = SCAN_PAGE_UP;
1445           break;
1446         default :
1447           break;
1448         }
1449       }
1450 
1451       switch (UnicodeChar) {
1452       case 'R':
1453         if (TerminalDevice->ResetState == RESET_STATE_DEFAULT) {
1454           TerminalDevice->ResetState = RESET_STATE_ESC_R;
1455           SetDefaultResetState = FALSE;
1456         } else if (TerminalDevice->ResetState == RESET_STATE_ESC_R_ESC_R) {
1457           gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
1458         }
1459         Key.ScanCode = SCAN_NULL;
1460         break;
1461       case 'r':
1462         if (TerminalDevice->ResetState == RESET_STATE_ESC_R) {
1463           TerminalDevice->ResetState = RESET_STATE_ESC_R_ESC_R;
1464           SetDefaultResetState = FALSE;
1465         }
1466         Key.ScanCode = SCAN_NULL;
1467         break;
1468       default :
1469         break;
1470       }
1471 
1472       if (SetDefaultResetState) {
1473         TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1474       }
1475 
1476       if (Key.ScanCode != SCAN_NULL) {
1477         Key.UnicodeChar = 0;
1478         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1479         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1480         UnicodeToEfiKeyFlushState (TerminalDevice);
1481         continue;
1482       }
1483 
1484       UnicodeToEfiKeyFlushState (TerminalDevice);
1485 
1486       break;
1487 
1488     case INPUT_STATE_ESC | INPUT_STATE_O:
1489 
1490       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1491 
1492       Key.ScanCode = SCAN_NULL;
1493 
1494       if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1495         switch (UnicodeChar) {
1496         case 'P':
1497           Key.ScanCode = SCAN_F1;
1498           break;
1499         case 'Q':
1500           Key.ScanCode = SCAN_F2;
1501           break;
1502         case 'w':
1503           Key.ScanCode = SCAN_F3;
1504           break;
1505         case 'x':
1506           Key.ScanCode = SCAN_F4;
1507           break;
1508         case 't':
1509           Key.ScanCode = SCAN_F5;
1510           break;
1511         case 'u':
1512           Key.ScanCode = SCAN_F6;
1513           break;
1514         case 'q':
1515           Key.ScanCode = SCAN_F7;
1516           break;
1517         case 'r':
1518           Key.ScanCode = SCAN_F8;
1519           break;
1520         case 'p':
1521           Key.ScanCode = SCAN_F9;
1522           break;
1523         case 'M':
1524           Key.ScanCode = SCAN_F10;
1525           break;
1526         default :
1527           break;
1528         }
1529       } else if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1530         /* Also accept VT100 escape codes for F1-F4, HOME and END for TTY term */
1531         switch (UnicodeChar) {
1532         case 'P':
1533           Key.ScanCode = SCAN_F1;
1534           break;
1535         case 'Q':
1536           Key.ScanCode = SCAN_F2;
1537           break;
1538         case 'R':
1539           Key.ScanCode = SCAN_F3;
1540           break;
1541         case 'S':
1542           Key.ScanCode = SCAN_F4;
1543           break;
1544         case 'H':
1545           Key.ScanCode = SCAN_HOME;
1546           break;
1547         case 'F':
1548           Key.ScanCode = SCAN_END;
1549           break;
1550         }
1551       }
1552 
1553       if (Key.ScanCode != SCAN_NULL) {
1554         Key.UnicodeChar = 0;
1555         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1556         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1557         UnicodeToEfiKeyFlushState (TerminalDevice);
1558         continue;
1559       }
1560 
1561       UnicodeToEfiKeyFlushState (TerminalDevice);
1562 
1563       break;
1564 
1565     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET:
1566 
1567       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1568 
1569       Key.ScanCode = SCAN_NULL;
1570 
1571       if (TerminalDevice->TerminalType == TerminalTypePcAnsi    ||
1572           TerminalDevice->TerminalType == TerminalTypeVt100     ||
1573           TerminalDevice->TerminalType == TerminalTypeVt100Plus ||
1574           TerminalDevice->TerminalType == TerminalTypeVtUtf8    ||
1575           TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1576         switch (UnicodeChar) {
1577         case 'A':
1578           Key.ScanCode = SCAN_UP;
1579           break;
1580         case 'B':
1581           Key.ScanCode = SCAN_DOWN;
1582           break;
1583         case 'C':
1584           Key.ScanCode = SCAN_RIGHT;
1585           break;
1586         case 'D':
1587           Key.ScanCode = SCAN_LEFT;
1588           break;
1589         case 'H':
1590           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1591               TerminalDevice->TerminalType == TerminalTypeVt100  ||
1592               TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1593             Key.ScanCode = SCAN_HOME;
1594           }
1595           break;
1596         case 'F':
1597           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1598               TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1599             Key.ScanCode = SCAN_END;
1600           }
1601           break;
1602         case 'K':
1603           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1604             Key.ScanCode = SCAN_END;
1605           }
1606           break;
1607         case 'L':
1608         case '@':
1609           if (TerminalDevice->TerminalType == TerminalTypePcAnsi ||
1610               TerminalDevice->TerminalType == TerminalTypeVt100) {
1611             Key.ScanCode = SCAN_INSERT;
1612           }
1613           break;
1614         case 'X':
1615           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1616             Key.ScanCode = SCAN_DELETE;
1617           }
1618           break;
1619         case 'P':
1620           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1621             Key.ScanCode = SCAN_DELETE;
1622           } else if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1623             Key.ScanCode = SCAN_F4;
1624           }
1625           break;
1626         case 'I':
1627           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1628             Key.ScanCode = SCAN_PAGE_UP;
1629           }
1630           break;
1631         case 'V':
1632           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1633             Key.ScanCode = SCAN_F10;
1634           }
1635           break;
1636         case '?':
1637           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1638             Key.ScanCode = SCAN_PAGE_UP;
1639           }
1640           break;
1641         case 'G':
1642           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1643             Key.ScanCode = SCAN_PAGE_DOWN;
1644           }
1645           break;
1646         case 'U':
1647           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1648             Key.ScanCode = SCAN_F9;
1649           }
1650           break;
1651         case '/':
1652           if (TerminalDevice->TerminalType == TerminalTypeVt100) {
1653             Key.ScanCode = SCAN_PAGE_DOWN;
1654           }
1655           break;
1656         case 'M':
1657           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1658             Key.ScanCode = SCAN_F1;
1659           }
1660           break;
1661         case 'N':
1662           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1663             Key.ScanCode = SCAN_F2;
1664           }
1665           break;
1666         case 'O':
1667           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1668             Key.ScanCode = SCAN_F3;
1669           }
1670           break;
1671         case 'Q':
1672           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1673             Key.ScanCode = SCAN_F5;
1674           }
1675           break;
1676         case 'R':
1677           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1678             Key.ScanCode = SCAN_F6;
1679           }
1680           break;
1681         case 'S':
1682           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1683             Key.ScanCode = SCAN_F7;
1684           }
1685           break;
1686         case 'T':
1687           if (TerminalDevice->TerminalType == TerminalTypePcAnsi) {
1688             Key.ScanCode = SCAN_F8;
1689           }
1690           break;
1691         default :
1692           break;
1693         }
1694       }
1695 
1696       /*
1697        * The VT220 escape codes that the TTY terminal accepts all have
1698        * numeric codes, and there are no ambiguous prefixes shared with
1699        * other terminal types.
1700        */
1701       if (TerminalDevice->TerminalType == TerminalTypeTtyTerm &&
1702           Key.ScanCode == SCAN_NULL &&
1703           UnicodeChar >= '0' &&
1704           UnicodeChar <= '9') {
1705         TerminalDevice->TtyEscapeStr[0] = UnicodeChar;
1706         TerminalDevice->TtyEscapeIndex = 1;
1707         TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET_2;
1708         continue;
1709       }
1710 
1711       if (Key.ScanCode != SCAN_NULL) {
1712         Key.UnicodeChar = 0;
1713         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1714         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1715         UnicodeToEfiKeyFlushState (TerminalDevice);
1716         continue;
1717       }
1718 
1719       UnicodeToEfiKeyFlushState (TerminalDevice);
1720 
1721       break;
1722 
1723 
1724     case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_LEFTOPENBRACKET_2:
1725       /*
1726        * Here we handle the VT220 escape codes that we accept.  This
1727        * state is only used by the TTY terminal type.
1728        */
1729       Key.ScanCode = SCAN_NULL;
1730       if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1731 
1732         if (UnicodeChar == '~' && TerminalDevice->TtyEscapeIndex <= 2) {
1733           UINT16 EscCode;
1734           TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex] = 0; /* Terminate string */
1735           EscCode = (UINT16) StrDecimalToUintn(TerminalDevice->TtyEscapeStr);
1736           switch (EscCode) {
1737           case 2:
1738               Key.ScanCode = SCAN_INSERT;
1739               break;
1740           case 3:
1741               Key.ScanCode = SCAN_DELETE;
1742               break;
1743           case 5:
1744               Key.ScanCode = SCAN_PAGE_UP;
1745               break;
1746           case 6:
1747               Key.ScanCode = SCAN_PAGE_DOWN;
1748               break;
1749           case 11:
1750           case 12:
1751           case 13:
1752           case 14:
1753           case 15:
1754             Key.ScanCode = SCAN_F1 + EscCode - 11;
1755             break;
1756           case 17:
1757           case 18:
1758           case 19:
1759           case 20:
1760           case 21:
1761             Key.ScanCode = SCAN_F6 + EscCode - 17;
1762             break;
1763           case 23:
1764           case 24:
1765             Key.ScanCode = SCAN_F11 + EscCode - 23;
1766             break;
1767           default:
1768             break;
1769           }
1770         } else if (TerminalDevice->TtyEscapeIndex == 1){
1771           /* 2 character escape code   */
1772           TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex++] = UnicodeChar;
1773           continue;
1774         }
1775         else {
1776           DEBUG ((EFI_D_ERROR, "Unexpected state in escape2\n"));
1777         }
1778       }
1779       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1780 
1781       if (Key.ScanCode != SCAN_NULL) {
1782         Key.UnicodeChar = 0;
1783         EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1784         TerminalDevice->InputState = INPUT_STATE_DEFAULT;
1785         UnicodeToEfiKeyFlushState (TerminalDevice);
1786         continue;
1787       }
1788 
1789       UnicodeToEfiKeyFlushState (TerminalDevice);
1790       break;
1791 
1792     default:
1793       //
1794       // Invalid state. This should never happen.
1795       //
1796       ASSERT (FALSE);
1797 
1798       UnicodeToEfiKeyFlushState (TerminalDevice);
1799 
1800       break;
1801     }
1802 
1803     if (UnicodeChar == ESC) {
1804       TerminalDevice->InputState = INPUT_STATE_ESC;
1805     }
1806 
1807     if (UnicodeChar == CSI) {
1808       TerminalDevice->InputState = INPUT_STATE_CSI;
1809     }
1810 
1811     if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) {
1812       Status = gBS->SetTimer(
1813                       TerminalDevice->TwoSecondTimeOut,
1814                       TimerRelative,
1815                       (UINT64)20000000
1816                       );
1817       ASSERT_EFI_ERROR (Status);
1818       continue;
1819     }
1820 
1821     if (SetDefaultResetState) {
1822       TerminalDevice->ResetState = RESET_STATE_DEFAULT;
1823     }
1824 
1825     if (UnicodeChar == DEL) {
1826       if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) {
1827         Key.ScanCode    = SCAN_NULL;
1828         Key.UnicodeChar = CHAR_BACKSPACE;
1829       }
1830       else {
1831         Key.ScanCode    = SCAN_DELETE;
1832         Key.UnicodeChar = 0;
1833       }
1834     } else {
1835       Key.ScanCode    = SCAN_NULL;
1836       Key.UnicodeChar = UnicodeChar;
1837     }
1838 
1839     EfiKeyFiFoInsertOneKey (TerminalDevice, &Key);
1840   }
1841 }
1842