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