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