f491de6e9523312f486ad51dd71f2ebb88a6c349
[mirror_edk2.git] / EmbeddedPkg / SimpleTextInOutSerial / SimpleTextInOut.c
1 /** @file
2 Simple Console that sits on a SerialLib.
3
4 Copyright (c) 2008-2009, Apple Inc. All rights reserved.
5
6 All rights reserved. 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
68 #include <Protocol/SerialIo.h>
69 #include <Protocol/SimpleTextIn.h>
70 #include <Protocol/SimpleTextOut.h>
71
72
73 #define MODE0_COLUMN_COUNT 80
74 #define MODE0_ROW_COUNT 25
75
76
77 EFI_STATUS
78 EFIAPI
79 TextInReset(
80 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
81 IN BOOLEAN ExtendedVerification
82 );
83
84
85 EFI_STATUS
86 EFIAPI
87 ReadKeyStroke(
88 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
89 OUT EFI_INPUT_KEY *Key
90 );
91
92
93 EFI_STATUS
94 EFIAPI
95 TextOutReset(
96 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
97 IN BOOLEAN ExtendedVerification
98 );
99
100 CHAR8 *
101 EFIAPI
102 SafeUnicodeStrToAsciiStr (
103 IN CONST CHAR16 *Source,
104 OUT CHAR8 *Destination
105 );
106
107 EFI_STATUS
108 EFIAPI
109 OutputString (
110 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
111 IN CHAR16 *String
112 );
113
114
115 EFI_STATUS
116 EFIAPI
117 TestString (
118 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
119 IN CHAR16 *String
120 );
121
122
123 EFI_STATUS
124 EFIAPI
125 QueryMode (
126 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
127 IN UINTN ModeNumber,
128 OUT UINTN *Columns,
129 OUT UINTN *Rows
130 );
131
132
133 EFI_STATUS
134 EFIAPI
135 SetMode(
136 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
137 IN UINTN ModeNumber
138 );
139
140
141 EFI_STATUS
142 EFIAPI
143 SetAttribute(
144 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
145 IN UINTN Attribute
146 );
147
148
149 EFI_STATUS
150 EFIAPI
151 ClearScreen (
152 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
153 );
154
155
156 EFI_STATUS
157 EFIAPI
158 SetCursorPosition (
159 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
160 IN UINTN Column,
161 IN UINTN Row
162 );
163
164
165 EFI_STATUS
166 EFIAPI
167 EnableCursor (
168 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
169 IN BOOLEAN Enable
170 );
171
172
173 EFI_SIMPLE_TEXT_INPUT_PROTOCOL mSimpleTextIn = {
174 TextInReset,
175 ReadKeyStroke,
176 NULL
177 };
178
179 EFI_SIMPLE_TEXT_OUTPUT_MODE mSimpleTextOutMode = {
180 1,
181 0,
182 EFI_TEXT_ATTR( EFI_LIGHTGRAY, EFI_BLACK ),
183 0,
184 0,
185 TRUE
186 };
187
188 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL mSimpleTextOut = {
189 TextOutReset,
190 OutputString,
191 TestString,
192 QueryMode,
193 SetMode,
194 SetAttribute,
195 ClearScreen,
196 SetCursorPosition,
197 EnableCursor,
198 &mSimpleTextOutMode
199 };
200
201 EFI_HANDLE mInstallHandle = NULL;
202
203
204
205 BOOLEAN
206 TextOutIsValidAscii (
207 IN CHAR16 Ascii
208 )
209 {
210 //
211 // valid ASCII code lies in the extent of 0x20 - 0x7F
212 //
213 if ((Ascii >= 0x20) && (Ascii <= 0x7F)) {
214 return TRUE;
215 }
216
217 return FALSE;
218 }
219
220
221 BOOLEAN
222 TextOutIsValidEfiCntlChar (
223 IN CHAR16 Char
224 )
225 {
226 //
227 // only support four control characters.
228 //
229 if (Char == CHAR_NULL ||
230 Char == CHAR_BACKSPACE ||
231 Char == CHAR_LINEFEED ||
232 Char == CHAR_CARRIAGE_RETURN ||
233 Char == CHAR_TAB ) {
234 return TRUE;
235 }
236
237 return FALSE;
238 }
239
240
241 VOID
242 EFIAPI
243 WaitForKeyEvent (
244 IN EFI_EVENT Event,
245 IN VOID *Context
246 )
247 {
248 if (SerialPortPoll ()) {
249 gBS->SignalEvent (Event);
250 }
251 }
252
253
254 EFI_STATUS
255 EFIAPI
256 TextInReset (
257 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
258 IN BOOLEAN ExtendedVerification
259 )
260 {
261 return EFI_SUCCESS;
262 }
263
264
265 EFI_STATUS
266 EFIAPI
267 ReadKeyStroke (
268 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
269 OUT EFI_INPUT_KEY *Key
270 )
271 {
272 CHAR8 Char;
273
274 SerialPortRead ((UINT8 *)&Char, 1);
275
276 //
277 // Check for ESC sequence. This code is not techincally correct VT100 code.
278 // An illegal ESC sequence represents an ESC and the characters that follow.
279 // This code will eat one or two chars after an escape. This is done to
280 // prevent some complex FIFOing of the data. It is good enough to get
281 // the arrow and delete keys working
282 //
283 Key->UnicodeChar = 0;
284 Key->ScanCode = SCAN_NULL;
285 if (Char == 0x1b) {
286 SerialPortRead ((UINT8 *)&Char, 1);
287 if (Char == '[') {
288 SerialPortRead ((UINT8 *)&Char, 1);
289 switch (Char) {
290 case 'A':
291 Key->ScanCode = SCAN_UP;
292 break;
293 case 'B':
294 Key->ScanCode = SCAN_DOWN;
295 break;
296 case 'C':
297 Key->ScanCode = SCAN_RIGHT;
298 break;
299 case 'D':
300 Key->ScanCode = SCAN_LEFT;
301 break;
302 case 'H':
303 Key->ScanCode = SCAN_HOME;
304 break;
305 case 'K':
306 case 'F': // PC ANSI
307 Key->ScanCode = SCAN_END;
308 break;
309 case '@':
310 case 'L':
311 Key->ScanCode = SCAN_INSERT;
312 break;
313 case 'P':
314 case 'X': // PC ANSI
315 Key->ScanCode = SCAN_DELETE;
316 break;
317 case 'U':
318 case '/':
319 case 'G': // PC ANSI
320 Key->ScanCode = SCAN_PAGE_DOWN;
321 break;
322 case 'V':
323 case '?':
324 case 'I': // PC ANSI
325 Key->ScanCode = SCAN_PAGE_UP;
326 break;
327
328 // PCANSI that does not conflict with VT100
329 case 'M':
330 Key->ScanCode = SCAN_F1;
331 break;
332 case 'N':
333 Key->ScanCode = SCAN_F2;
334 break;
335 case 'O':
336 Key->ScanCode = SCAN_F3;
337 break;
338 case 'Q':
339 Key->ScanCode = SCAN_F5;
340 break;
341 case 'R':
342 Key->ScanCode = SCAN_F6;
343 break;
344 case 'S':
345 Key->ScanCode = SCAN_F7;
346 break;
347 case 'T':
348 Key->ScanCode = SCAN_F8;
349 break;
350
351 default:
352 Key->UnicodeChar = Char;
353 break;
354 }
355 } else if (Char == '0') {
356 SerialPortRead ((UINT8 *)&Char, 1);
357 switch (Char) {
358 case 'P':
359 Key->ScanCode = SCAN_F1;
360 break;
361 case 'Q':
362 Key->ScanCode = SCAN_F2;
363 break;
364 case 'w':
365 Key->ScanCode = SCAN_F3;
366 break;
367 case 'x':
368 Key->ScanCode = SCAN_F4;
369 break;
370 case 't':
371 Key->ScanCode = SCAN_F5;
372 break;
373 case 'u':
374 Key->ScanCode = SCAN_F6;
375 break;
376 case 'q':
377 Key->ScanCode = SCAN_F7;
378 break;
379 case 'r':
380 Key->ScanCode = SCAN_F8;
381 break;
382 case 'p':
383 Key->ScanCode = SCAN_F9;
384 break;
385 case 'm':
386 Key->ScanCode = SCAN_F10;
387 break;
388 default :
389 break;
390 }
391 }
392 } else if (Char < ' ') {
393 if ((Char == CHAR_BACKSPACE) ||
394 (Char == CHAR_TAB) ||
395 (Char == CHAR_LINEFEED) ||
396 (Char == CHAR_CARRIAGE_RETURN)) {
397 // Only let through EFI required control characters
398 Key->UnicodeChar = (CHAR16)Char;
399 }
400 } else if (Char == 0x7f) {
401 Key->ScanCode = SCAN_DELETE;
402 } else {
403 Key->UnicodeChar = (CHAR16)Char;
404 }
405
406 return EFI_SUCCESS;
407 }
408
409
410 EFI_STATUS
411 EFIAPI
412 TextOutReset (
413 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
414 IN BOOLEAN ExtendedVerification
415 )
416 {
417 EFI_STATUS Status;
418
419 This->SetAttribute(
420 This,
421 EFI_TEXT_ATTR(This->Mode->Attribute & 0x0F, EFI_BACKGROUND_BLACK)
422 );
423
424 Status = This->SetMode (This, 0);
425
426 return Status;
427 }
428
429 CHAR8 *
430 EFIAPI
431 SafeUnicodeStrToAsciiStr (
432 IN CONST CHAR16 *Source,
433 OUT CHAR8 *Destination
434 )
435 {
436 CHAR8 *ReturnValue;
437
438 ASSERT (Destination != NULL);
439
440 //
441 // ASSERT if Source is long than PcdMaximumUnicodeStringLength.
442 // Length tests are performed inside StrLen().
443 //
444 ASSERT (StrSize (Source) != 0);
445
446 //
447 // Source and Destination should not overlap
448 //
449 ASSERT ((UINTN) ((CHAR16 *) Destination - Source) > StrLen (Source));
450 ASSERT ((UINTN) ((CHAR8 *) Source - Destination) > StrLen (Source));
451
452
453 ReturnValue = Destination;
454 while (*Source != '\0') {
455 //
456 // If any non-ascii characters in Source then replace it with '?'.
457 //
458 if (*Source < 0x80) {
459 *Destination = (CHAR8) *Source;
460 } else {
461 *Destination = '?';
462
463 //Surrogate pair check.
464 if ((*Source >= 0xD800) && (*Source <= 0xDFFF)) {
465 Source++;
466 }
467 }
468
469 Destination++;
470 Source++;
471 }
472
473 *Destination = '\0';
474
475 //
476 // ASSERT Original Destination is less long than PcdMaximumAsciiStringLength.
477 // Length tests are performed inside AsciiStrLen().
478 //
479 ASSERT (AsciiStrSize (ReturnValue) != 0);
480
481 return ReturnValue;
482 }
483
484 EFI_STATUS
485 EFIAPI
486 OutputString (
487 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
488 IN CHAR16 *String
489 )
490 {
491 UINTN Size = StrLen(String) + 1;
492 CHAR8 *OutputString = AllocatePool(Size);
493
494 //If there is any non-ascii characters in String buffer then replace it with '?'
495 //Eventually, UnicodeStrToAsciiStr API should be fixed.
496 SafeUnicodeStrToAsciiStr(String, OutputString);
497 SerialPortWrite ((UINT8 *)OutputString, Size - 1);
498
499 FreePool(OutputString);
500
501 return EFI_SUCCESS;
502 }
503
504
505 EFI_STATUS
506 EFIAPI
507 TestString (
508 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
509 IN CHAR16 *String
510 )
511 {
512 CHAR8 Character;
513
514 for ( ; *String != CHAR_NULL; String++) {
515 Character = (CHAR8)*String;
516 if (!(TextOutIsValidAscii (Character) || TextOutIsValidEfiCntlChar (Character))) {
517 return EFI_UNSUPPORTED;
518 }
519 }
520
521 return EFI_SUCCESS;
522 }
523
524
525 EFI_STATUS
526 EFIAPI
527 QueryMode (
528 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
529 IN UINTN ModeNumber,
530 OUT UINTN *Columns,
531 OUT UINTN *Rows
532 )
533 {
534 if (This->Mode->MaxMode > 1) {
535 return EFI_DEVICE_ERROR;
536 }
537
538 if (ModeNumber == 0) {
539 *Columns = MODE0_COLUMN_COUNT;
540 *Rows = MODE0_ROW_COUNT;
541 return EFI_SUCCESS;
542 }
543
544 return EFI_UNSUPPORTED;
545 }
546
547
548 EFI_STATUS
549 EFIAPI
550 SetMode (
551 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
552 IN UINTN ModeNumber
553 )
554 {
555 if (ModeNumber != 0) {
556 return EFI_UNSUPPORTED;
557 }
558
559 This->Mode->Mode = 0;
560 This->ClearScreen (This);
561 return EFI_SUCCESS;
562 }
563
564
565 EFI_STATUS
566 EFIAPI
567 SetAttribute(
568 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
569 IN UINTN Attribute
570 )
571 {
572 This->Mode->Attribute = (INT32)Attribute;
573 return EFI_SUCCESS;
574 }
575
576
577 EFI_STATUS
578 EFIAPI
579 ClearScreen (
580 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
581 )
582 {
583 EFI_STATUS Status;
584
585 Status = This->SetCursorPosition (This, 0, 0);
586 return Status;
587 }
588
589
590 EFI_STATUS
591 EFIAPI
592 SetCursorPosition (
593 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
594 IN UINTN Column,
595 IN UINTN Row
596 )
597 {
598 EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;
599 EFI_STATUS Status;
600 UINTN MaxColumn;
601 UINTN MaxRow;
602
603 Mode = This->Mode;
604
605 Status = This->QueryMode(
606 This,
607 Mode->Mode,
608 &MaxColumn,
609 &MaxRow
610 );
611 if (EFI_ERROR(Status)) {
612 return EFI_UNSUPPORTED;
613 }
614
615 if ((Column >= MaxColumn) || (Row >= MaxRow)) {
616 return EFI_UNSUPPORTED;
617 }
618
619 Mode->CursorColumn = (INT32)Column;
620 Mode->CursorRow = (INT32)Row;
621
622 return EFI_SUCCESS;
623 }
624
625
626 EFI_STATUS
627 EFIAPI
628 EnableCursor (
629 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
630 IN BOOLEAN Enable
631 )
632 {
633 if (!Enable) {
634 return EFI_UNSUPPORTED;
635 }
636
637 return EFI_SUCCESS;
638 }
639
640
641 EFI_STATUS
642 EFIAPI
643 SimpleTextInOutEntryPoint (
644 IN EFI_HANDLE ImageHandle,
645 IN EFI_SYSTEM_TABLE *SystemTable
646 )
647 {
648 EFI_STATUS Status;
649
650 Status = gBS->CreateEvent (
651 EVT_NOTIFY_WAIT,
652 TPL_NOTIFY,
653 WaitForKeyEvent,
654 NULL,
655 &mSimpleTextIn.WaitForKey
656 );
657 ASSERT_EFI_ERROR (Status);
658
659 Status = gBS->InstallMultipleProtocolInterfaces(
660 &mInstallHandle,
661 &gEfiSimpleTextInProtocolGuid, &mSimpleTextIn,
662 &gEfiSimpleTextOutProtocolGuid, &mSimpleTextOut,
663 NULL
664 );
665 if (!EFI_ERROR (Status)) {
666 gST->ConOut = &mSimpleTextOut;
667 gST->ConIn = &mSimpleTextIn;
668 }
669
670 return Status;
671 }