blob: f491de6e9523312f486ad51dd71f2ebb88a6c349 [file] [log] [blame]
AJFISH2ef2b012009-12-06 01:57:05 +00001/** @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
77EFI_STATUS
78EFIAPI
79TextInReset(
80 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
81 IN BOOLEAN ExtendedVerification
82 );
83
84
85EFI_STATUS
86EFIAPI
87ReadKeyStroke(
88 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
89 OUT EFI_INPUT_KEY *Key
90 );
91
92
93EFI_STATUS
94EFIAPI
95TextOutReset(
96 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
97 IN BOOLEAN ExtendedVerification
98 );
99
100CHAR8 *
101EFIAPI
102SafeUnicodeStrToAsciiStr (
103 IN CONST CHAR16 *Source,
104 OUT CHAR8 *Destination
105 );
106
107EFI_STATUS
108EFIAPI
109OutputString (
110 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
111 IN CHAR16 *String
112 );
113
114
115EFI_STATUS
116EFIAPI
117TestString (
118 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
119 IN CHAR16 *String
120 );
121
122
123EFI_STATUS
124EFIAPI
125QueryMode (
126 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
127 IN UINTN ModeNumber,
128 OUT UINTN *Columns,
129 OUT UINTN *Rows
130 );
131
132
133EFI_STATUS
134EFIAPI
135SetMode(
136 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
137 IN UINTN ModeNumber
138 );
139
140
141EFI_STATUS
142EFIAPI
143SetAttribute(
144 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
145 IN UINTN Attribute
146 );
147
148
149EFI_STATUS
150EFIAPI
151ClearScreen (
152 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
153 );
154
155
156EFI_STATUS
157EFIAPI
158SetCursorPosition (
159 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
160 IN UINTN Column,
161 IN UINTN Row
162 );
163
164
165EFI_STATUS
166EFIAPI
167EnableCursor (
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
188EFI_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
205BOOLEAN
206TextOutIsValidAscii (
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
221BOOLEAN
222TextOutIsValidEfiCntlChar (
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
241VOID
242EFIAPI
243WaitForKeyEvent (
244 IN EFI_EVENT Event,
245 IN VOID *Context
246 )
247{
248 if (SerialPortPoll ()) {
249 gBS->SignalEvent (Event);
250 }
251}
252
253
254EFI_STATUS
255EFIAPI
256TextInReset (
257 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
258 IN BOOLEAN ExtendedVerification
259 )
260{
261 return EFI_SUCCESS;
262}
263
264
265EFI_STATUS
266EFIAPI
267ReadKeyStroke (
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
410EFI_STATUS
411EFIAPI
412TextOutReset (
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
429CHAR8 *
430EFIAPI
431SafeUnicodeStrToAsciiStr (
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
484EFI_STATUS
485EFIAPI
486OutputString (
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
505EFI_STATUS
506EFIAPI
507TestString (
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
525EFI_STATUS
526EFIAPI
527QueryMode (
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
548EFI_STATUS
549EFIAPI
550SetMode (
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
565EFI_STATUS
566EFIAPI
567SetAttribute(
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
577EFI_STATUS
578EFIAPI
579ClearScreen (
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
590EFI_STATUS
591EFIAPI
592SetCursorPosition (
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
626EFI_STATUS
627EFIAPI
628EnableCursor (
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
641EFI_STATUS
642EFIAPI
643SimpleTextInOutEntryPoint (
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}