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