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