8df58fce22071a1a6ea3d21039bacce2303f04c6
[mirror_edk2.git] / SimpleTextInOut.c
1 /** @file
2 Simple Console that sits on a SerialLib.
3
4 Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 /*
17 Symbols used in table below
18 ===========================
19 ESC = 0x1B
20 CSI = 0x9B
21 DEL = 0x7f
22 ^ = CTRL
23
24 +=========+======+===========+==========+==========+
25 | | EFI | UEFI 2.0 | | |
26 | | Scan | | VT100+ | |
27 | KEY | Code | PC ANSI | VTUTF8 | VT100 |
28 +=========+======+===========+==========+==========+
29 | NULL | 0x00 | | | |
30 | UP | 0x01 | ESC [ A | ESC [ A | ESC [ A |
31 | DOWN | 0x02 | ESC [ B | ESC [ B | ESC [ B |
32 | RIGHT | 0x03 | ESC [ C | ESC [ C | ESC [ C |
33 | LEFT | 0x04 | ESC [ D | ESC [ D | ESC [ D |
34 | HOME | 0x05 | ESC [ H | ESC h | ESC [ H |
35 | END | 0x06 | ESC [ F | ESC k | ESC [ K |
36 | INSERT | 0x07 | ESC [ @ | ESC + | ESC [ @ |
37 | | | ESC [ L | | ESC [ L |
38 | DELETE | 0x08 | ESC [ X | ESC - | ESC [ P |
39 | PG UP | 0x09 | ESC [ I | ESC ? | ESC [ V |
40 | | | | | ESC [ ? |
41 | PG DOWN | 0x0A | ESC [ G | ESC / | ESC [ U |
42 | | | | | ESC [ / |
43 | F1 | 0x0B | ESC [ M | ESC 1 | ESC O P |
44 | F2 | 0x0C | ESC [ N | ESC 2 | ESC O Q |
45 | F3 | 0x0D | ESC [ O | ESC 3 | ESC O w |
46 | F4 | 0x0E | ESC [ P | ESC 4 | ESC O x |
47 | F5 | 0x0F | ESC [ Q | ESC 5 | ESC O t |
48 | F6 | 0x10 | ESC [ R | ESC 6 | ESC O u |
49 | F7 | 0x11 | ESC [ S | ESC 7 | ESC O q |
50 | F8 | 0x12 | ESC [ T | ESC 8 | ESC O r |
51 | F9 | 0x13 | ESC [ U | ESC 9 | ESC O p |
52 | F10 | 0x14 | ESC [ V | ESC 0 | ESC O M |
53 | Escape | 0x17 | ESC | ESC | ESC |
54 | F11 | 0x15 | | ESC ! | |
55 | F12 | 0x16 | | ESC @ | |
56 +=========+======+===========+==========+==========+
57
58 */
59
60 #include <PiDxe.h>
61 #include <Library/UefiLib.h>
62 #include <Library/UefiBootServicesTableLib.h>
63 #include <Library/BaseLib.h>
64 #include <Library/MemoryAllocationLib.h>
65 #include <Library/DebugLib.h>
66 #include <Library/SerialPortLib.h>
67 #include <Library/PcdLib.h>
68
69 #include <Protocol/SerialIo.h>
70 #include <Protocol/SimpleTextIn.h>
71 #include <Protocol/SimpleTextOut.h>
72 #include <Protocol/DevicePath.h>
73
74
75 #define MODE0_COLUMN_COUNT 80
76 #define MODE0_ROW_COUNT 25
77
78
79 EFI_STATUS
80 EFIAPI
81 TextInReset(
82 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
83 IN BOOLEAN ExtendedVerification
84 );
85
86
87 EFI_STATUS
88 EFIAPI
89 ReadKeyStroke(
90 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
91 OUT EFI_INPUT_KEY *Key
92 );
93
94
95 EFI_STATUS
96 EFIAPI
97 TextOutReset(
98 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
99 IN BOOLEAN ExtendedVerification
100 );
101
102 CHAR8 *
103 EFIAPI
104 SafeUnicodeStrToAsciiStr (
105 IN CONST CHAR16 *Source,
106 OUT CHAR8 *Destination
107 );
108
109 EFI_STATUS
110 EFIAPI
111 OutputString (
112 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
113 IN CHAR16 *String
114 );
115
116
117 EFI_STATUS
118 EFIAPI
119 TestString (
120 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
121 IN CHAR16 *String
122 );
123
124
125 EFI_STATUS
126 EFIAPI
127 QueryMode (
128 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
129 IN UINTN ModeNumber,
130 OUT UINTN *Columns,
131 OUT UINTN *Rows
132 );
133
134
135 EFI_STATUS
136 EFIAPI
137 SetMode(
138 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
139 IN UINTN ModeNumber
140 );
141
142
143 EFI_STATUS
144 EFIAPI
145 SetAttribute(
146 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
147 IN UINTN Attribute
148 );
149
150
151 EFI_STATUS
152 EFIAPI
153 ClearScreen (
154 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
155 );
156
157
158 EFI_STATUS
159 EFIAPI
160 SetCursorPosition (
161 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
162 IN UINTN Column,
163 IN UINTN Row
164 );
165
166
167 EFI_STATUS
168 EFIAPI
169 EnableCursor (
170 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
171 IN BOOLEAN Enable
172 );
173
174
175 EFI_SIMPLE_TEXT_INPUT_PROTOCOL mSimpleTextIn = {
176 TextInReset,
177 ReadKeyStroke,
178 NULL
179 };
180
181 EFI_SIMPLE_TEXT_OUTPUT_MODE mSimpleTextOutMode = {
182 1,
183 0,
184 EFI_TEXT_ATTR( EFI_LIGHTGRAY, EFI_BLACK ),
185 0,
186 0,
187 TRUE
188 };
189
190 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL mSimpleTextOut = {
191 TextOutReset,
192 OutputString,
193 TestString,
194 QueryMode,
195 SetMode,
196 SetAttribute,
197 ClearScreen,
198 SetCursorPosition,
199 EnableCursor,
200 &mSimpleTextOutMode
201 };
202
203 EFI_HANDLE mInstallHandle = NULL;
204
205 typedef struct {
206 VENDOR_DEVICE_PATH Guid;
207 UART_DEVICE_PATH Uart;
208 EFI_DEVICE_PATH_PROTOCOL End;
209 } SIMPLE_TEXT_OUT_DEVICE_PATH;
210
211 SIMPLE_TEXT_OUT_DEVICE_PATH mDevicePath = {
212 {
213 { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0} },
214 EFI_CALLER_ID_GUID
215 },
216 {
217 { MESSAGING_DEVICE_PATH, MSG_UART_DP, { sizeof (UART_DEVICE_PATH), 0} },
218 0, // Reserved
219 FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate
220 FixedPcdGet8 (PcdUartDefaultDataBits), // DataBits
221 FixedPcdGet8 (PcdUartDefaultParity), // Parity (N)
222 FixedPcdGet8 (PcdUartDefaultStopBits) // StopBits
223 },
224 { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0} }
225 };
226
227
228
229
230 BOOLEAN
231 TextOutIsValidAscii (
232 IN CHAR16 Ascii
233 )
234 {
235 //
236 // valid ASCII code lies in the extent of 0x20 - 0x7F
237 //
238 if ((Ascii >= 0x20) && (Ascii <= 0x7F)) {
239 return TRUE;
240 }
241
242 return FALSE;
243 }
244
245
246 BOOLEAN
247 TextOutIsValidEfiCntlChar (
248 IN CHAR16 Char
249 )
250 {
251 //
252 // only support four control characters.
253 //
254 if (Char == CHAR_NULL ||
255 Char == CHAR_BACKSPACE ||
256 Char == CHAR_LINEFEED ||
257 Char == CHAR_CARRIAGE_RETURN ||
258 Char == CHAR_TAB ) {
259 return TRUE;
260 }
261
262 return FALSE;
263 }
264
265
266 VOID
267 EFIAPI
268 WaitForKeyEvent (
269 IN EFI_EVENT Event,
270 IN VOID *Context
271 )
272 {
273 if (SerialPortPoll ()) {
274 gBS->SignalEvent (Event);
275 }
276 }
277
278
279 EFI_STATUS
280 EFIAPI
281 TextInReset (
282 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
283 IN BOOLEAN ExtendedVerification
284 )
285 {
286 return EFI_SUCCESS;
287 }
288
289
290 EFI_STATUS
291 EFIAPI
292 ReadKeyStroke (
293 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
294 OUT EFI_INPUT_KEY *Key
295 )
296 {
297 CHAR8 Char;
298
299 if (!SerialPortPoll ()) {
300 return EFI_NOT_READY;
301 }
302
303 SerialPortRead ((UINT8 *)&Char, 1);
304
305 //
306 // Check for ESC sequence. This code is not techincally correct VT100 code.
307 // An illegal ESC sequence represents an ESC and the characters that follow.
308 // This code will eat one or two chars after an escape. This is done to
309 // prevent some complex FIFOing of the data. It is good enough to get
310 // the arrow and delete keys working
311 //
312 Key->UnicodeChar = 0;
313 Key->ScanCode = SCAN_NULL;
314 if (Char == 0x1b) {
315 SerialPortRead ((UINT8 *)&Char, 1);
316 if (Char == '[') {
317 SerialPortRead ((UINT8 *)&Char, 1);
318 switch (Char) {
319 case 'A':
320 Key->ScanCode = SCAN_UP;
321 break;
322 case 'B':
323 Key->ScanCode = SCAN_DOWN;
324 break;
325 case 'C':
326 Key->ScanCode = SCAN_RIGHT;
327 break;
328 case 'D':
329 Key->ScanCode = SCAN_LEFT;
330 break;
331 case 'H':
332 Key->ScanCode = SCAN_HOME;
333 break;
334 case 'K':
335 case 'F': // PC ANSI
336 Key->ScanCode = SCAN_END;
337 break;
338 case '@':
339 case 'L':
340 Key->ScanCode = SCAN_INSERT;
341 break;
342 case 'P':
343 case 'X': // PC ANSI
344 Key->ScanCode = SCAN_DELETE;
345 break;
346 case 'U':
347 case '/':
348 case 'G': // PC ANSI
349 Key->ScanCode = SCAN_PAGE_DOWN;
350 break;
351 case 'V':
352 case '?':
353 case 'I': // PC ANSI
354 Key->ScanCode = SCAN_PAGE_UP;
355 break;
356
357 // PCANSI that does not conflict with VT100
358 case 'M':
359 Key->ScanCode = SCAN_F1;
360 break;
361 case 'N':
362 Key->ScanCode = SCAN_F2;
363 break;
364 case 'O':
365 Key->ScanCode = SCAN_F3;
366 break;
367 case 'Q':
368 Key->ScanCode = SCAN_F5;
369 break;
370 case 'R':
371 Key->ScanCode = SCAN_F6;
372 break;
373 case 'S':
374 Key->ScanCode = SCAN_F7;
375 break;
376 case 'T':
377 Key->ScanCode = SCAN_F8;
378 break;
379
380 default:
381 Key->UnicodeChar = Char;
382 break;
383 }
384 } else if (Char == '0') {
385 SerialPortRead ((UINT8 *)&Char, 1);
386 switch (Char) {
387 case 'P':
388 Key->ScanCode = SCAN_F1;
389 break;
390 case 'Q':
391 Key->ScanCode = SCAN_F2;
392 break;
393 case 'w':
394 Key->ScanCode = SCAN_F3;
395 break;
396 case 'x':
397 Key->ScanCode = SCAN_F4;
398 break;
399 case 't':
400 Key->ScanCode = SCAN_F5;
401 break;
402 case 'u':
403 Key->ScanCode = SCAN_F6;
404 break;
405 case 'q':
406 Key->ScanCode = SCAN_F7;
407 break;
408 case 'r':
409 Key->ScanCode = SCAN_F8;
410 break;
411 case 'p':
412 Key->ScanCode = SCAN_F9;
413 break;
414 case 'm':
415 Key->ScanCode = SCAN_F10;
416 break;
417 default :
418 break;
419 }
420 }
421 } else if (Char < ' ') {
422 if ((Char == CHAR_BACKSPACE) ||
423 (Char == CHAR_TAB) ||
424 (Char == CHAR_LINEFEED) ||
425 (Char == CHAR_CARRIAGE_RETURN)) {
426 // Only let through EFI required control characters
427 Key->UnicodeChar = (CHAR16)Char;
428 }
429 } else if (Char == 0x7f) {
430 Key->ScanCode = SCAN_DELETE;
431 } else {
432 Key->UnicodeChar = (CHAR16)Char;
433 }
434
435 return EFI_SUCCESS;
436 }
437
438
439 EFI_STATUS
440 EFIAPI
441 TextOutReset (
442 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
443 IN BOOLEAN ExtendedVerification
444 )
445 {
446 EFI_STATUS Status;
447
448 This->SetAttribute(
449 This,
450 EFI_TEXT_ATTR(This->Mode->Attribute & 0x0F, EFI_BACKGROUND_BLACK)
451 );
452
453 Status = This->SetMode (This, 0);
454
455 return Status;
456 }
457
458 CHAR8 *
459 EFIAPI
460 SafeUnicodeStrToAsciiStr (
461 IN CONST CHAR16 *Source,
462 OUT CHAR8 *Destination
463 )
464 {
465 CHAR8 *ReturnValue;
466
467 ASSERT (Destination != NULL);
468
469 //
470 // ASSERT if Source is long than PcdMaximumUnicodeStringLength.
471 // Length tests are performed inside StrLen().
472 //
473 ASSERT (StrSize (Source) != 0);
474
475 //
476 // Source and Destination should not overlap
477 //
478 ASSERT ((UINTN) ((CHAR16 *) Destination - Source) > StrLen (Source));
479 ASSERT ((UINTN) ((CHAR8 *) Source - Destination) > StrLen (Source));
480
481
482 ReturnValue = Destination;
483 while (*Source != '\0') {
484 //
485 // If any non-ascii characters in Source then replace it with '?'.
486 //
487 if (*Source < 0x80) {
488 *Destination = (CHAR8) *Source;
489 } else {
490 *Destination = '?';
491
492 //Surrogate pair check.
493 if ((*Source >= 0xD800) && (*Source <= 0xDFFF)) {
494 Source++;
495 }
496 }
497
498 Destination++;
499 Source++;
500 }
501
502 *Destination = '\0';
503
504 //
505 // ASSERT Original Destination is less long than PcdMaximumAsciiStringLength.
506 // Length tests are performed inside AsciiStrLen().
507 //
508 ASSERT (AsciiStrSize (ReturnValue) != 0);
509
510 return ReturnValue;
511 }
512
513 EFI_STATUS
514 EFIAPI
515 OutputString (
516 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
517 IN CHAR16 *String
518 )
519 {
520 UINTN Size;
521 CHAR8* OutputString;
522 EFI_STATUS Status;
523 EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;
524 UINTN MaxColumn;
525 UINTN MaxRow;
526
527 Size = StrLen(String) + 1;
528 OutputString = AllocatePool(Size);
529
530 //If there is any non-ascii characters in String buffer then replace it with '?'
531 //Eventually, UnicodeStrToAsciiStr API should be fixed.
532 SafeUnicodeStrToAsciiStr(String, OutputString);
533 SerialPortWrite ((UINT8 *)OutputString, Size - 1);
534
535 //
536 // Parse each character of the string to output
537 // to update the cursor position information
538 //
539 Mode = This->Mode;
540
541 Status = This->QueryMode (
542 This,
543 Mode->Mode,
544 &MaxColumn,
545 &MaxRow
546 );
547 if (EFI_ERROR (Status)) {
548 return Status;
549 }
550
551 for (; *String != CHAR_NULL; String++) {
552
553 switch (*String) {
554 case CHAR_BACKSPACE:
555 if (Mode->CursorColumn > 0) {
556 Mode->CursorColumn--;
557 }
558 break;
559
560 case CHAR_LINEFEED:
561 if (Mode->CursorRow < (INT32) (MaxRow - 1)) {
562 Mode->CursorRow++;
563 }
564 break;
565
566 case CHAR_CARRIAGE_RETURN:
567 Mode->CursorColumn = 0;
568 break;
569
570 default:
571 if (Mode->CursorColumn >= (INT32) (MaxColumn - 1)) {
572 // Move the cursor as if we print CHAR_CARRIAGE_RETURN & CHAR_LINE_FEED
573 // CHAR_LINEFEED
574 if (Mode->CursorRow < (INT32) (MaxRow - 1)) {
575 Mode->CursorRow++;
576 }
577 // CHAR_CARIAGE_RETURN
578 Mode->CursorColumn = 0;
579 } else {
580 Mode->CursorColumn++;
581 }
582 break;
583 }
584 }
585
586 FreePool(OutputString);
587
588 return EFI_SUCCESS;
589 }
590
591
592 EFI_STATUS
593 EFIAPI
594 TestString (
595 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
596 IN CHAR16 *String
597 )
598 {
599 CHAR8 Character;
600
601 for ( ; *String != CHAR_NULL; String++) {
602 Character = (CHAR8)*String;
603 if (!(TextOutIsValidAscii (Character) || TextOutIsValidEfiCntlChar (Character))) {
604 return EFI_UNSUPPORTED;
605 }
606 }
607
608 return EFI_SUCCESS;
609 }
610
611
612 EFI_STATUS
613 EFIAPI
614 QueryMode (
615 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
616 IN UINTN ModeNumber,
617 OUT UINTN *Columns,
618 OUT UINTN *Rows
619 )
620 {
621 if (This->Mode->MaxMode > 1) {
622 return EFI_DEVICE_ERROR;
623 }
624
625 if (ModeNumber == 0) {
626 *Columns = MODE0_COLUMN_COUNT;
627 *Rows = MODE0_ROW_COUNT;
628 return EFI_SUCCESS;
629 }
630
631 return EFI_UNSUPPORTED;
632 }
633
634
635 EFI_STATUS
636 EFIAPI
637 SetMode (
638 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
639 IN UINTN ModeNumber
640 )
641 {
642 if (ModeNumber != 0) {
643 return EFI_UNSUPPORTED;
644 }
645
646 This->Mode->Mode = 0;
647 This->ClearScreen (This);
648 return EFI_SUCCESS;
649 }
650
651
652 EFI_STATUS
653 EFIAPI
654 SetAttribute(
655 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
656 IN UINTN Attribute
657 )
658 {
659 This->Mode->Attribute = (INT32)Attribute;
660 return EFI_SUCCESS;
661 }
662
663
664 EFI_STATUS
665 EFIAPI
666 ClearScreen (
667 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
668 )
669 {
670 EFI_STATUS Status;
671
672 Status = This->SetCursorPosition (This, 0, 0);
673 return Status;
674 }
675
676
677 EFI_STATUS
678 EFIAPI
679 SetCursorPosition (
680 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
681 IN UINTN Column,
682 IN UINTN Row
683 )
684 {
685 EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;
686 EFI_STATUS Status;
687 UINTN MaxColumn;
688 UINTN MaxRow;
689
690 Mode = This->Mode;
691
692 Status = This->QueryMode(
693 This,
694 Mode->Mode,
695 &MaxColumn,
696 &MaxRow
697 );
698 if (EFI_ERROR(Status)) {
699 return EFI_UNSUPPORTED;
700 }
701
702 if ((Column >= MaxColumn) || (Row >= MaxRow)) {
703 return EFI_UNSUPPORTED;
704 }
705
706 Mode->CursorColumn = (INT32)Column;
707 Mode->CursorRow = (INT32)Row;
708
709 return EFI_SUCCESS;
710 }
711
712
713 EFI_STATUS
714 EFIAPI
715 EnableCursor (
716 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
717 IN BOOLEAN Enable
718 )
719 {
720 if (!Enable) {
721 return EFI_UNSUPPORTED;
722 }
723
724 return EFI_SUCCESS;
725 }
726
727
728 EFI_STATUS
729 EFIAPI
730 SimpleTextInOutEntryPoint (
731 IN EFI_HANDLE ImageHandle,
732 IN EFI_SYSTEM_TABLE *SystemTable
733 )
734 {
735 EFI_STATUS Status;
736
737 Status = gBS->CreateEvent (
738 EVT_NOTIFY_WAIT,
739 TPL_NOTIFY,
740 WaitForKeyEvent,
741 NULL,
742 &mSimpleTextIn.WaitForKey
743 );
744 ASSERT_EFI_ERROR (Status);
745
746 Status = gBS->InstallMultipleProtocolInterfaces(
747 &mInstallHandle,
748 &gEfiSimpleTextInProtocolGuid, &mSimpleTextIn,
749 &gEfiSimpleTextOutProtocolGuid, &mSimpleTextOut,
750 &gEfiDevicePathProtocolGuid, &mDevicePath,
751 NULL
752 );
753 if (!EFI_ERROR (Status)) {
754 gST->ConOut = &mSimpleTextOut;
755 gST->ConIn = &mSimpleTextIn;
756 }
757
758 return Status;
759 }