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
);
81 wc
[0] = BLOCKELEMENT_LIGHT_SHADE
;
87 *dest
++ = (CHAR16
)wc
[0];
100 IN
struct __filedes
*filp
105 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
106 // Quick check to see if Stream looks reasonable
107 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
108 EFIerrno
= RETURN_INVALID_PARAMETER
;
109 return -1; // Looks like a bad File Descriptor pointer
111 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
112 return RETURN_SUCCESS
;
119 struct __filedes
*filp
,
121 int whence
///< Ignored by Console
125 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
128 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
129 // Quick check to see if Stream looks reasonable
130 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
131 EFIerrno
= RETURN_INVALID_PARAMETER
;
132 return -1; // Looks like a bad This pointer
134 if(Stream
->InstanceNum
== STDIN_FILENO
) {
135 // Seek is not valid for stdin
136 EFIerrno
= RETURN_UNSUPPORTED
;
139 // Everything is OK to do the final verification and "seek".
140 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
141 CursorPos
.Offset
= Position
;
143 EFIerrno
= Proto
->SetCursorPosition(Proto
,
144 (INTN
)CursorPos
.XYpos
.Column
,
145 (INTN
)CursorPos
.XYpos
.Row
);
147 if(RETURN_ERROR(EFIerrno
)) {
155 /* Write a NULL terminated WCS to the EFI console.
157 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
158 the string couldn't be displayed.
159 @param[in] Buffer The WCS string to be displayed
161 @return The number of BYTES written. Because of MBCS, this may be more than number of characters.
167 IN
struct __filedes
*filp
,
169 IN
size_t BufferSize
,
170 IN
const void *Buffer
174 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
178 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
179 // Quick check to see if Stream looks reasonable
180 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
181 EFIerrno
= RETURN_INVALID_PARAMETER
;
182 return -1; // Looks like a bad This pointer
184 if(Stream
->InstanceNum
== STDIN_FILENO
) {
185 // Write is not valid for stdin
186 EFIerrno
= RETURN_UNSUPPORTED
;
189 // Everything is OK to do the write.
190 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
192 // Convert string from MBCS to WCS and translate \n to \r\n.
193 NumBytes
= WideTtyCvt(gMD
->UString
, (const char *)Buffer
, (ssize_t
)BufferSize
, &Stream
->CharState
);
194 BufferSize
= NumBytes
;
197 // Send the Unicode buffer to the console
198 Status
= Proto
->OutputString( Proto
, gMD
->UString
);
199 // Depending on status, update BufferSize and return
200 if(RETURN_ERROR(Status
)) {
201 BufferSize
= 0; // We don't really know how many characters made it out
204 //BufferSize = NumBytes;
205 Stream
->NumWritten
+= NumBytes
;
207 EFIerrno
= Status
; // Make error reason available to caller
211 /** Read characters from the console input device.
213 @param[in,out] filp Pointer to file descriptor for this file.
214 @param[in,out] offset Ignored.
215 @param[in] BufferSize Buffer size, in bytes.
216 @param[out] Buffer Buffer in which to place the read characters.
218 @return Number of bytes actually placed into Buffer.
220 @todo Handle encodings other than ASCII-7 and UEFI.
226 IN OUT
struct __filedes
*filp
,
227 IN OUT off_t
*offset
, // Console ignores this
228 IN
size_t BufferSize
,
232 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
238 EFI_STATUS Status
= RETURN_SUCCESS
;
240 char EchoBuff
[MB_CUR_MAX
+ 1];
243 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
244 // Quick check to see if Stream looks reasonable
245 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
246 EFIerrno
= RETURN_INVALID_PARAMETER
;
247 return -1; // Looks like a bad This pointer
249 if(Stream
->InstanceNum
!= STDIN_FILENO
) {
250 // Read only valid for stdin
251 EFIerrno
= RETURN_UNSUPPORTED
;
254 // It looks like things are OK for trying to read
255 // We will accumulate *BufferSize characters or until we encounter
256 // an "activation" character. Currently any control character.
257 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
259 NumChar
= (BufferSize
> MAX_INPUT
)? MAX_INPUT
: BufferSize
;
262 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
263 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
264 if(Status
!= RETURN_SUCCESS
) {
267 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
268 if(Status
!= RETURN_SUCCESS
) {
273 Key
.ScanCode
= Stream
->UnGetKey
.ScanCode
;
274 Key
.UnicodeChar
= Stream
->UnGetKey
.UnicodeChar
;
275 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
276 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
278 if(Key
.ScanCode
== SCAN_NULL
) {
280 if(TtyCooked
&& (Key
.UnicodeChar
== CHAR_CARRIAGE_RETURN
)) {
281 *OutPtr
++ = CHAR_LINEFEED
;
282 NumEcho
= wctomb(EchoBuff
, CHAR_LINEFEED
);
285 *OutPtr
++ = Key
.UnicodeChar
;
286 NumEcho
= wctomb(EchoBuff
, Key
.UnicodeChar
);
289 EchoBuff
[NumEcho
] = 0; /* Terminate the Echo buffer */
291 /* Echo the character just input */
292 da_ConWrite(&gMD
->fdarray
[STDOUT_FILENO
], NULL
, 2, EchoBuff
);
295 if(iswcntrl(Key
.UnicodeChar
)) { // If a control character, or a scan code
298 } while(i
< NumChar
);
300 *OutPtr
= L
'\0'; // Terminate the input buffer
302 /* Convert the input buffer and place in Buffer.
303 If the fully converted input buffer won't fit, write what will and
304 leave the rest in ConReadBuf with ConReadLeft indicating how many
305 unconverted characters remain in ConReadBuf.
307 NumEcho
= (int)wcstombs(Buffer
, ConReadBuf
, BufferSize
); /* Re-use NumEcho to hold number of bytes in Buffer */
308 /* More work needs to be done before locales other than C can be supported. */
311 return (ssize_t
)NumEcho
; // Will be 0 if we didn't get a key
314 /** Console-specific helper function for the fstat() function.
316 st_size Set to number of characters read for stdin and number written for stdout and stderr.
317 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
318 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
319 st_blksize Set to 1 since this is a character device
321 All other members of the stat structure are left unchanged.
327 struct __filedes
*filp
,
333 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
340 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
341 // Quick check to see if Stream looks reasonable
342 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
345 EFIerrno
= RETURN_INVALID_PARAMETER
;
348 // All of our parameters are correct, so fill in the information.
349 Buffer
->st_blksize
= 0; // Character device, not a block device
350 Buffer
->st_mode
= filp
->f_iflags
;
353 if(Stream
->InstanceNum
== STDIN_FILENO
) {
355 Buffer
->st_curpos
= 0;
356 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
357 Buffer
->st_physsize
= 1;
360 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
361 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
362 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
363 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
364 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
366 OutMode
= Proto
->Mode
->Mode
;
367 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
368 if(RETURN_ERROR(EFIerrno
)) {
369 Buffer
->st_physsize
= 0;
372 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
373 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
374 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
384 struct __filedes
*filp
,
392 /** Open an abstract Console Device.
398 struct __filedes
*filp
,
399 int DevInstance
, // Not used for console devices
400 wchar_t *Path
, // Not used for console devices
401 wchar_t *MPath
// Not used for console devices
409 EFIerrno
= RETURN_INVALID_PARAMETER
;
413 Stream
= (ConInstance
*)DevNode
->InstanceList
;
414 // Quick check to see if Stream looks reasonable
415 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
416 EFIerrno
= RETURN_INVALID_PARAMETER
;
418 return -1; // Looks like a bad This pointer
420 gMD
->StdIo
[Stream
->InstanceNum
] = Stream
;
421 filp
->f_iflags
|= (S_IFREG
| _S_IFCHR
| _S_ICONSOLE
);
423 filp
->f_ops
= &Stream
->Abstraction
;
428 #include <sys/poll.h>
429 /* Returns a bit mask describing which operations could be completed immediately.
431 (POLLIN | POLLRDNORM) A Unicode character is available to read
432 (POLLIN) A ScanCode is ready.
433 (POLLOUT) The device is ready for output - always set on stdout and stderr.
440 struct __filedes
*filp
,
444 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
446 EFI_STATUS Status
= RETURN_SUCCESS
;
449 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
450 // Quick check to see if Stream looks reasonable
451 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
452 EFIerrno
= RETURN_INVALID_PARAMETER
;
453 return POLLNVAL
; // Looks like a bad filp pointer
455 if(Stream
->InstanceNum
== 0) {
456 // Only input is supported for this device
457 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
458 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
459 Status
= Proto
->ReadKeyStroke(Proto
, &Stream
->UnGetKey
);
460 if(Status
== RETURN_SUCCESS
) {
462 if(Stream
->UnGetKey
.UnicodeChar
!= CHAR_NULL
) {
463 RdyMask
|= POLLRDNORM
;
467 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
468 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
472 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
473 // Only output is supported for this device
477 RdyMask
= POLLERR
; // Not one of the standard streams
481 return (RdyMask
& (events
| POLL_RETONLY
));
484 /** Construct the Console stream devices: stdin, stdout, stderr.
486 Allocate the instance structure and populate it with the information for
492 IN EFI_HANDLE ImageHandle
,
493 IN EFI_SYSTEM_TABLE
*SystemTable
497 RETURN_STATUS Status
= RETURN_SUCCESS
;
500 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
501 ConReadBuf
= (wchar_t *)AllocateZeroPool((MAX_INPUT
+ 1) * sizeof(wchar_t));
502 if((ConInstanceList
== NULL
) || (ConReadBuf
== NULL
)) {
503 return RETURN_OUT_OF_RESOURCES
;
506 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
507 // Get pointer to instance.
508 Stream
= &ConInstanceList
[i
];
510 Stream
->Cookie
= CON_COOKIE
;
511 Stream
->InstanceNum
= i
;
512 Stream
->CharState
.A
= 0; // Start in the initial state
516 Stream
->Dev
= SystemTable
->ConIn
;
519 Stream
->Dev
= SystemTable
->ConOut
;
522 if(SystemTable
->StdErr
== NULL
) {
523 Stream
->Dev
= SystemTable
->ConOut
;
526 Stream
->Dev
= SystemTable
->StdErr
;
530 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
533 Stream
->Abstraction
.fo_close
= &da_ConClose
;
534 Stream
->Abstraction
.fo_read
= &da_ConRead
;
535 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
536 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
537 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
538 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
539 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
540 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
541 Stream
->Abstraction
.fo_flush
= &fnullop_flush
;
542 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
543 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
544 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
545 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
548 Stream
->NumWritten
= 0;
549 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
550 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
552 if(Stream
->Dev
== NULL
) {
553 continue; // No device for this stream.
555 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
, 1, sizeof(ConInstance
), stdioFlags
[i
]);
556 if(ConNode
[i
] == NULL
) {
560 Stream
->Parent
= ConNode
[i
];
562 /* Initialize Ioctl flags until Ioctl is really implemented. */
572 IN EFI_HANDLE ImageHandle
,
573 IN EFI_SYSTEM_TABLE
*SystemTable
578 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
579 if(ConNode
[i
] != NULL
) {
580 FreePool(ConNode
[i
]);
583 if(ConInstanceList
!= NULL
) {
584 FreePool(ConInstanceList
);
586 if(ConReadBuf
!= NULL
) {
587 FreePool(ConReadBuf
);
590 return RETURN_SUCCESS
;
593 /* ######################################################################### */
594 #if 0 /* Not implemented for Console */
600 struct __filedes
*filp
,
612 struct __filedes
*filp
617 #endif /* Not implemented for Console */