2 Abstract device driver for the UEFI Console.
4 Manipulates abstractions for stdin, stdout, stderr.
6 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials are licensed and made available under
8 the terms and conditions of the BSD License that accompanies this distribution.
9 The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php.
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <Library/BaseLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Protocol/SimpleTextIn.h>
21 #include <Protocol/SimpleTextOut.h>
23 #include <LibConfig.h>
24 #include <sys/EfiSysCall.h>
29 #include <sys/fcntl.h>
31 #include <Device/Device.h>
34 static const CHAR16
* const
35 stdioNames
[NUM_SPECIAL
] = {
36 L
"stdin:", L
"stdout:", L
"stderr:"
39 static const int stdioFlags
[NUM_SPECIAL
] = {
45 static DeviceNode
*ConNode
[NUM_SPECIAL
];
46 static ConInstance
*ConInstanceList
;
48 static wchar_t *ConReadBuf
;
50 /* Flags settable by Ioctl */
51 static BOOLEAN TtyCooked
;
52 static BOOLEAN TtyEcho
;
55 WideTtyCvt( CHAR16
*dest
, const char *buf
, size_t n
)
60 for(i
= 0; i
< n
; ++i
) {
66 wc
= BLOCKELEMENT_LIGHT_SHADE
;
81 IN
struct __filedes
*filp
86 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
87 // Quick check to see if Stream looks reasonable
88 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
89 EFIerrno
= RETURN_INVALID_PARAMETER
;
90 return -1; // Looks like a bad File Descriptor pointer
92 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
93 return RETURN_SUCCESS
;
100 struct __filedes
*filp
,
102 int whence
///< Ignored by Console
106 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
109 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
110 // Quick check to see if Stream looks reasonable
111 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
112 EFIerrno
= RETURN_INVALID_PARAMETER
;
113 return -1; // Looks like a bad This pointer
115 if(Stream
->InstanceNum
== STDIN_FILENO
) {
116 // Seek is not valid for stdin
117 EFIerrno
= RETURN_UNSUPPORTED
;
120 // Everything is OK to do the final verification and "seek".
121 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
122 CursorPos
.Offset
= Position
;
124 EFIerrno
= Proto
->SetCursorPosition(Proto
,
125 (INTN
)CursorPos
.XYpos
.Column
,
126 (INTN
)CursorPos
.XYpos
.Row
);
128 if(RETURN_ERROR(EFIerrno
)) {
136 /* Write a NULL terminated WCS to the EFI console.
138 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
139 the string couldn't be displayed.
140 @param[in] Buffer The WCS string to be displayed
142 @return The number of characters written.
148 IN
struct __filedes
*filp
,
150 IN
size_t BufferSize
,
151 IN
const void *Buffer
155 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
158 //XYoffset CursorPos;
160 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
161 // Quick check to see if Stream looks reasonable
162 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
163 EFIerrno
= RETURN_INVALID_PARAMETER
;
164 return -1; // Looks like a bad This pointer
166 if(Stream
->InstanceNum
== STDIN_FILENO
) {
167 // Write is not valid for stdin
168 EFIerrno
= RETURN_UNSUPPORTED
;
171 // Everything is OK to do the write.
172 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
174 // Convert string from MBCS to WCS and translate \n to \r\n.
175 NumChar
= WideTtyCvt(gMD
->UString
, (const char *)Buffer
, BufferSize
);
177 // BufferSize = (size_t)(NumChar * sizeof(CHAR16));
179 BufferSize
= NumChar
;
181 //if( Position != NULL) {
182 // CursorPos.Offset = (UINT64)*Position;
184 // Status = Proto->SetCursorPosition(Proto,
185 // (INTN)CursorPos.XYpos.Column,
186 // (INTN)CursorPos.XYpos.Row);
187 // if(RETURN_ERROR(Status)) {
192 // Send the Unicode buffer to the console
193 Status
= Proto
->OutputString( Proto
, gMD
->UString
);
194 // Depending on status, update BufferSize and return
195 if(RETURN_ERROR(Status
)) {
196 BufferSize
= 0; // We don't really know how many characters made it out
199 //BufferSize = NumChar;
200 Stream
->NumWritten
+= NumChar
;
206 /** Read characters from the console input device.
208 @param[in,out] filp Pointer to file descriptor for this file.
209 @param[in,out] offset Ignored.
210 @param[in] BufferSize Buffer size, in bytes.
211 @param[out] Buffer Buffer in which to place the read characters.
213 @return Number of bytes actually placed into Buffer.
215 @todo Handle encodings other than ASCII-7 and UEFI.
221 IN OUT
struct __filedes
*filp
,
222 IN OUT off_t
*offset
, // Console ignores this
223 IN
size_t BufferSize
,
227 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
233 EFI_STATUS Status
= RETURN_SUCCESS
;
235 char EchoBuff
[MB_CUR_MAX
+ 1];
238 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
239 // Quick check to see if Stream looks reasonable
240 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
241 EFIerrno
= RETURN_INVALID_PARAMETER
;
242 return -1; // Looks like a bad This pointer
244 if(Stream
->InstanceNum
!= STDIN_FILENO
) {
245 // Read only valid for stdin
246 EFIerrno
= RETURN_UNSUPPORTED
;
249 // It looks like things are OK for trying to read
250 // We will accumulate *BufferSize characters or until we encounter
251 // an "activation" character. Currently any control character.
252 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
254 NumChar
= (BufferSize
> MAX_INPUT
)? MAX_INPUT
: BufferSize
;
257 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
258 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
259 if(Status
!= RETURN_SUCCESS
) {
262 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
263 if(Status
!= RETURN_SUCCESS
) {
268 Key
.ScanCode
= Stream
->UnGetKey
.ScanCode
;
269 Key
.UnicodeChar
= Stream
->UnGetKey
.UnicodeChar
;
270 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
271 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
273 if(Key
.ScanCode
== SCAN_NULL
) {
275 if(TtyCooked
&& (Key
.UnicodeChar
== CHAR_CARRIAGE_RETURN
)) {
276 *OutPtr
++ = CHAR_LINEFEED
;
277 NumEcho
= wctomb(EchoBuff
, CHAR_LINEFEED
);
280 *OutPtr
++ = Key
.UnicodeChar
;
281 NumEcho
= wctomb(EchoBuff
, Key
.UnicodeChar
);
284 EchoBuff
[NumEcho
] = 0; /* Terminate the Echo buffer */
286 /* Echo the character just input */
287 da_ConWrite(&gMD
->fdarray
[STDOUT_FILENO
], NULL
, 2, EchoBuff
);
290 if(iswcntrl(Key
.UnicodeChar
)) { // If a control character, or a scan code
293 } while(i
< NumChar
);
295 *OutPtr
= L
'\0'; // Terminate the input buffer
297 /* Convert the input buffer and place in Buffer.
298 If the fully converted input buffer won't fit, write what will and
299 leave the rest in ConReadBuf with ConReadLeft indicating how many
300 unconverted characters remain in ConReadBuf.
302 NumEcho
= (int)wcstombs(Buffer
, ConReadBuf
, BufferSize
); /* Re-use NumEcho to hold number of bytes in Buffer */
303 /* More work needs to be done before locales other than C can be supported. */
306 return (ssize_t
)NumEcho
; // Will be 0 if we didn't get a key
309 /** Console-specific helper function for the fstat() function.
311 st_size Set to number of characters read for stdin and number written for stdout and stderr.
312 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
313 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
314 st_blksize Set to 1 since this is a character device
316 All other members of the stat structure are left unchanged.
322 struct __filedes
*filp
,
328 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
335 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
336 // Quick check to see if Stream looks reasonable
337 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
340 EFIerrno
= RETURN_INVALID_PARAMETER
;
343 // All of our parameters are correct, so fill in the information.
344 Buffer
->st_blksize
= 1;
347 if(Stream
->InstanceNum
== STDIN_FILENO
) {
349 Buffer
->st_curpos
= 0;
350 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
351 Buffer
->st_physsize
= 1;
354 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
355 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
356 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
357 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
358 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
360 OutMode
= Proto
->Mode
->Mode
;
361 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
362 if(RETURN_ERROR(EFIerrno
)) {
363 Buffer
->st_physsize
= 0;
366 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
367 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
368 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
378 struct __filedes
*filp
,
386 /** Open an abstract Console Device.
392 struct __filedes
*filp
,
393 int DevInstance
, // Not used for console devices
394 wchar_t *Path
, // Not used for console devices
395 wchar_t *MPath
// Not used for console devices
403 EFIerrno
= RETURN_INVALID_PARAMETER
;
406 Stream
= (ConInstance
*)DevNode
->InstanceList
;
407 // Quick check to see if Stream looks reasonable
408 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
409 EFIerrno
= RETURN_INVALID_PARAMETER
;
410 return -1; // Looks like a bad This pointer
412 gMD
->StdIo
[Stream
->InstanceNum
] = Stream
;
413 filp
->f_iflags
|= (S_IFREG
| _S_IFCHR
| _S_ICONSOLE
);
415 filp
->f_ops
= &Stream
->Abstraction
;
420 #include <sys/poll.h>
421 /* Returns a bit mask describing which operations could be completed immediately.
423 (POLLIN | POLLRDNORM) A Unicode character is available to read
424 (POLLIN) A ScanCode is ready.
425 (POLLOUT) The device is ready for output - always set on stdout and stderr.
432 struct __filedes
*filp
,
436 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
438 EFI_STATUS Status
= RETURN_SUCCESS
;
441 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
442 // Quick check to see if Stream looks reasonable
443 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
444 EFIerrno
= RETURN_INVALID_PARAMETER
;
445 return POLLNVAL
; // Looks like a bad filp pointer
447 if(Stream
->InstanceNum
== 0) {
448 // Only input is supported for this device
449 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
450 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
451 Status
= Proto
->ReadKeyStroke(Proto
, &Stream
->UnGetKey
);
452 if(Status
== RETURN_SUCCESS
) {
454 if(Stream
->UnGetKey
.UnicodeChar
!= CHAR_NULL
) {
455 RdyMask
|= POLLRDNORM
;
459 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
460 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
464 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
465 // Only output is supported for this device
469 RdyMask
= POLLERR
; // Not one of the standard streams
473 return (RdyMask
& (events
| POLL_RETONLY
));
476 /** Construct the Console stream devices: stdin, stdout, stderr.
478 Allocate the instance structure and populate it with the information for
484 IN EFI_HANDLE ImageHandle
,
485 IN EFI_SYSTEM_TABLE
*SystemTable
489 RETURN_STATUS Status
= RETURN_SUCCESS
;
492 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
493 ConReadBuf
= (wchar_t *)AllocateZeroPool((MAX_INPUT
+ 1) * sizeof(wchar_t));
494 if((ConInstanceList
== NULL
) || (ConReadBuf
== NULL
)) {
495 return RETURN_OUT_OF_RESOURCES
;
498 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
499 // Get pointer to instance.
500 Stream
= &ConInstanceList
[i
];
502 Stream
->Cookie
= CON_COOKIE
;
503 Stream
->InstanceNum
= i
;
507 Stream
->Dev
= SystemTable
->ConIn
;
510 Stream
->Dev
= SystemTable
->ConOut
;
513 if(SystemTable
->StdErr
== NULL
) {
514 Stream
->Dev
= SystemTable
->ConOut
;
517 Stream
->Dev
= SystemTable
->StdErr
;
521 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
524 Stream
->Abstraction
.fo_close
= &da_ConClose
;
525 Stream
->Abstraction
.fo_read
= &da_ConRead
;
526 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
527 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
528 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
529 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
530 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
531 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
532 Stream
->Abstraction
.fo_flush
= &fnullop_flush
;
533 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
534 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
535 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
536 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
539 Stream
->NumWritten
= 0;
540 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
541 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
543 if(Stream
->Dev
== NULL
) {
544 continue; // No device for this stream.
546 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
, 1, sizeof(ConInstance
), stdioFlags
[i
]);
547 if(ConNode
[i
] == NULL
) {
551 Stream
->Parent
= ConNode
[i
];
553 /* Initialize Ioctl flags until Ioctl is really implemented. */
563 IN EFI_HANDLE ImageHandle
,
564 IN EFI_SYSTEM_TABLE
*SystemTable
569 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
570 if(ConNode
[i
] != NULL
) {
571 FreePool(ConNode
[i
]);
574 if(ConInstanceList
!= NULL
) {
575 FreePool(ConInstanceList
);
577 if(ConReadBuf
!= NULL
) {
578 FreePool(ConReadBuf
);
581 return RETURN_SUCCESS
;
584 /* ######################################################################### */
585 #if 0 /* Not implemented for Console */
591 struct __filedes
*filp
,
603 struct __filedes
*filp
608 #endif /* Not implemented for Console */