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