2 Abstract device driver for the UEFI Console.
4 Manipulates abstractions for stdin, stdout, stderr.
6 Copyright (c) 2010 - 2012, 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>
29 #include <sys/fcntl.h>
32 #include <Device/Device.h>
35 static const CHAR16
* const
36 stdioNames
[NUM_SPECIAL
] = {
37 L
"stdin:", L
"stdout:", L
"stderr:"
40 static const int stdioFlags
[NUM_SPECIAL
] = {
46 static DeviceNode
*ConNode
[NUM_SPECIAL
];
47 static ConInstance
*ConInstanceList
;
49 static wchar_t *ConReadBuf
;
51 /* Flags settable by Ioctl */
52 static BOOLEAN TtyCooked
;
53 static BOOLEAN TtyEcho
;
55 /** Convert string from MBCS to WCS and translate \n to \r\n.
57 It is the caller's responsibility to ensure that dest is
58 large enough to hold the converted results. It is guaranteed
59 that there will be fewer than n characters placed in dest.
61 @param dest WCS buffer to receive the converted string.
62 @param buf MBCS string to convert to WCS.
63 @param n Number of BYTES contained in buf.
64 @param Cs Pointer to the character state object for this stream
66 @return The number of BYTES consumed from buf.
69 WideTtyCvt( CHAR16
*dest
, const char *buf
, ssize_t n
, mbstate_t *Cs
)
76 numB
= (int)mbrtowc(wc
, buf
, MIN(MB_LEN_MAX
,n
), Cs
);
80 if(numB
< 0) { // If an unconvertable character, replace it.
81 wc
[0] = BLOCKELEMENT_LIGHT_SHADE
;
88 *dest
++ = (CHAR16
)wc
[0];
101 IN
struct __filedes
*filp
106 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
107 // Quick check to see if Stream looks reasonable
108 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
109 EFIerrno
= RETURN_INVALID_PARAMETER
;
110 return -1; // Looks like a bad File Descriptor pointer
112 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
113 return RETURN_SUCCESS
;
120 struct __filedes
*filp
,
122 int whence
///< Ignored by Console
126 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
129 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
130 // Quick check to see if Stream looks reasonable
131 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
132 EFIerrno
= RETURN_INVALID_PARAMETER
;
133 return -1; // Looks like a bad This pointer
135 if(Stream
->InstanceNum
== STDIN_FILENO
) {
136 // Seek is not valid for stdin
137 EFIerrno
= RETURN_UNSUPPORTED
;
140 // Everything is OK to do the final verification and "seek".
141 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
142 CursorPos
.Offset
= Position
;
144 EFIerrno
= Proto
->SetCursorPosition(Proto
,
145 (INTN
)CursorPos
.XYpos
.Column
,
146 (INTN
)CursorPos
.XYpos
.Row
);
148 if(RETURN_ERROR(EFIerrno
)) {
156 /* Write a NULL terminated WCS to the EFI console.
158 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
159 the string couldn't be displayed.
160 @param[in] Buffer The WCS string to be displayed
162 @return The number of BYTES written. Because of MBCS, this may be more than number of characters.
168 IN
struct __filedes
*filp
,
170 IN
size_t BufferSize
,
171 IN
const void *Buffer
175 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
179 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
180 // Quick check to see if Stream looks reasonable
181 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
182 EFIerrno
= RETURN_INVALID_PARAMETER
;
183 return -1; // Looks like a bad This pointer
185 if(Stream
->InstanceNum
== STDIN_FILENO
) {
186 // Write is not valid for stdin
187 EFIerrno
= RETURN_UNSUPPORTED
;
190 // Everything is OK to do the write.
191 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
193 // Convert string from MBCS to WCS and translate \n to \r\n.
194 NumBytes
= WideTtyCvt(gMD
->UString
, (const char *)Buffer
, (ssize_t
)BufferSize
, &Stream
->CharState
);
195 BufferSize
= NumBytes
;
198 // Send the Unicode buffer to the console
199 Status
= Proto
->OutputString( Proto
, gMD
->UString
);
200 // Depending on status, update BufferSize and return
201 if(RETURN_ERROR(Status
)) {
202 BufferSize
= 0; // We don't really know how many characters made it out
205 //BufferSize = NumBytes;
206 Stream
->NumWritten
+= NumBytes
;
208 EFIerrno
= Status
; // Make error reason available to caller
212 /** Read characters from the console input device.
214 @param[in,out] filp Pointer to file descriptor for this file.
215 @param[in,out] offset Ignored.
216 @param[in] BufferSize Buffer size, in bytes.
217 @param[out] Buffer Buffer in which to place the read characters.
219 @return Number of bytes actually placed into Buffer.
221 @todo Handle encodings other than ASCII-7 and UEFI.
227 IN OUT
struct __filedes
*filp
,
228 IN OUT off_t
*offset
, // Console ignores this
229 IN
size_t BufferSize
,
233 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
239 EFI_STATUS Status
= RETURN_SUCCESS
;
241 char EchoBuff
[MB_CUR_MAX
+ 1];
244 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
245 // Quick check to see if Stream looks reasonable
246 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
247 EFIerrno
= RETURN_INVALID_PARAMETER
;
248 return -1; // Looks like a bad This pointer
250 if(Stream
->InstanceNum
!= STDIN_FILENO
) {
251 // Read only valid for stdin
252 EFIerrno
= RETURN_UNSUPPORTED
;
255 // It looks like things are OK for trying to read
256 // We will accumulate *BufferSize characters or until we encounter
257 // an "activation" character. Currently any control character.
258 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
260 NumChar
= (BufferSize
> MAX_INPUT
)? MAX_INPUT
: BufferSize
;
263 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
264 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
265 if(Status
!= RETURN_SUCCESS
) {
268 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
269 if(Status
!= RETURN_SUCCESS
) {
274 Key
.ScanCode
= Stream
->UnGetKey
.ScanCode
;
275 Key
.UnicodeChar
= Stream
->UnGetKey
.UnicodeChar
;
276 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
277 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
279 if(Key
.ScanCode
== SCAN_NULL
) {
281 if(TtyCooked
&& (Key
.UnicodeChar
== CHAR_CARRIAGE_RETURN
)) {
282 *OutPtr
++ = CHAR_LINEFEED
;
283 NumEcho
= wctomb(EchoBuff
, CHAR_LINEFEED
);
286 *OutPtr
++ = Key
.UnicodeChar
;
287 NumEcho
= wctomb(EchoBuff
, Key
.UnicodeChar
);
290 EchoBuff
[NumEcho
] = 0; /* Terminate the Echo buffer */
292 /* Echo the character just input */
293 da_ConWrite(&gMD
->fdarray
[STDOUT_FILENO
], NULL
, 2, EchoBuff
);
296 if(iswcntrl(Key
.UnicodeChar
)) { // If a control character, or a scan code
299 } while(i
< NumChar
);
301 *OutPtr
= L
'\0'; // Terminate the input buffer
303 /* Convert the input buffer and place in Buffer.
304 If the fully converted input buffer won't fit, write what will and
305 leave the rest in ConReadBuf with ConReadLeft indicating how many
306 unconverted characters remain in ConReadBuf.
308 NumEcho
= (int)wcstombs(Buffer
, ConReadBuf
, BufferSize
); /* Re-use NumEcho to hold number of bytes in Buffer */
309 /* More work needs to be done before locales other than C can be supported. */
312 return (ssize_t
)NumEcho
; // Will be 0 if we didn't get a key
315 /** Console-specific helper function for the fstat() function.
317 st_size Set to number of characters read for stdin and number written for stdout and stderr.
318 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
319 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
320 st_blksize Set to 1 since this is a character device
322 All other members of the stat structure are left unchanged.
328 struct __filedes
*filp
,
334 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
341 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
342 // Quick check to see if Stream looks reasonable
343 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
346 EFIerrno
= RETURN_INVALID_PARAMETER
;
349 // All of our parameters are correct, so fill in the information.
350 Buffer
->st_blksize
= 0; // Character device, not a block device
351 Buffer
->st_mode
= filp
->f_iflags
;
354 if(Stream
->InstanceNum
== STDIN_FILENO
) {
356 Buffer
->st_curpos
= 0;
357 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
358 Buffer
->st_physsize
= 1;
361 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
362 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
363 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
364 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
365 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
367 OutMode
= Proto
->Mode
->Mode
;
368 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
369 if(RETURN_ERROR(EFIerrno
)) {
370 Buffer
->st_physsize
= 0;
373 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
374 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
375 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
385 struct __filedes
*filp
,
393 /** Open an abstract Console Device.
399 struct __filedes
*filp
,
400 int DevInstance
, // Not used for console devices
401 wchar_t *Path
, // Not used for console devices
402 wchar_t *MPath
// Not used for console devices
410 EFIerrno
= RETURN_INVALID_PARAMETER
;
414 Stream
= (ConInstance
*)DevNode
->InstanceList
;
415 // Quick check to see if Stream looks reasonable
416 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
417 EFIerrno
= RETURN_INVALID_PARAMETER
;
419 return -1; // Looks like a bad This pointer
421 gMD
->StdIo
[Stream
->InstanceNum
] = Stream
;
422 filp
->f_iflags
|= (S_IFREG
| _S_IFCHR
| _S_ICONSOLE
);
424 filp
->f_ops
= &Stream
->Abstraction
;
429 #include <sys/poll.h>
430 /* Returns a bit mask describing which operations could be completed immediately.
432 (POLLIN | POLLRDNORM) A Unicode character is available to read
433 (POLLIN) A ScanCode is ready.
434 (POLLOUT) The device is ready for output - always set on stdout and stderr.
441 struct __filedes
*filp
,
445 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
447 EFI_STATUS Status
= RETURN_SUCCESS
;
450 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
451 // Quick check to see if Stream looks reasonable
452 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
453 EFIerrno
= RETURN_INVALID_PARAMETER
;
454 return POLLNVAL
; // Looks like a bad filp pointer
456 if(Stream
->InstanceNum
== 0) {
457 // Only input is supported for this device
458 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
459 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
460 Status
= Proto
->ReadKeyStroke(Proto
, &Stream
->UnGetKey
);
461 if(Status
== RETURN_SUCCESS
) {
463 if(Stream
->UnGetKey
.UnicodeChar
!= CHAR_NULL
) {
464 RdyMask
|= POLLRDNORM
;
468 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
469 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
473 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
474 // Only output is supported for this device
478 RdyMask
= POLLERR
; // Not one of the standard streams
482 return (RdyMask
& (events
| POLL_RETONLY
));
485 /** Construct the Console stream devices: stdin, stdout, stderr.
487 Allocate the instance structure and populate it with the information for
493 IN EFI_HANDLE ImageHandle
,
494 IN EFI_SYSTEM_TABLE
*SystemTable
498 RETURN_STATUS Status
= RETURN_SUCCESS
;
501 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
502 ConReadBuf
= (wchar_t *)AllocateZeroPool((MAX_INPUT
+ 1) * sizeof(wchar_t));
503 if((ConInstanceList
== NULL
) || (ConReadBuf
== NULL
)) {
504 return RETURN_OUT_OF_RESOURCES
;
507 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
508 // Get pointer to instance.
509 Stream
= &ConInstanceList
[i
];
511 Stream
->Cookie
= CON_COOKIE
;
512 Stream
->InstanceNum
= i
;
513 Stream
->CharState
.A
= 0; // Start in the initial state
517 Stream
->Dev
= SystemTable
->ConIn
;
520 Stream
->Dev
= SystemTable
->ConOut
;
523 if(SystemTable
->StdErr
== NULL
) {
524 Stream
->Dev
= SystemTable
->ConOut
;
527 Stream
->Dev
= SystemTable
->StdErr
;
531 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
534 Stream
->Abstraction
.fo_close
= &da_ConClose
;
535 Stream
->Abstraction
.fo_read
= &da_ConRead
;
536 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
537 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
538 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
539 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
540 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
541 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
542 Stream
->Abstraction
.fo_flush
= &fnullop_flush
;
543 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
544 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
545 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
546 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
549 Stream
->NumWritten
= 0;
550 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
551 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
553 if(Stream
->Dev
== NULL
) {
554 continue; // No device for this stream.
556 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
, 1, sizeof(ConInstance
), stdioFlags
[i
]);
557 if(ConNode
[i
] == NULL
) {
561 Stream
->Parent
= ConNode
[i
];
563 /* Initialize Ioctl flags until Ioctl is really implemented. */
573 IN EFI_HANDLE ImageHandle
,
574 IN EFI_SYSTEM_TABLE
*SystemTable
579 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
580 if(ConNode
[i
] != NULL
) {
581 FreePool(ConNode
[i
]);
584 if(ConInstanceList
!= NULL
) {
585 FreePool(ConInstanceList
);
587 if(ConReadBuf
!= NULL
) {
588 FreePool(ConReadBuf
);
591 return RETURN_SUCCESS
;
594 /* ######################################################################### */
595 #if 0 /* Not implemented for Console */
601 struct __filedes
*filp
,
613 struct __filedes
*filp
618 #endif /* Not implemented for Console */