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
;
49 WideTtyCvt( CHAR16
*dest
, const char *buf
, size_t n
)
54 for(i
= 0; i
< n
; ++i
) {
60 wc
= BLOCKELEMENT_LIGHT_SHADE
;
75 IN
struct __filedes
*filp
80 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
81 // Quick check to see if Stream looks reasonable
82 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
83 EFIerrno
= RETURN_INVALID_PARAMETER
;
84 return -1; // Looks like a bad File Descriptor pointer
86 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
87 return RETURN_SUCCESS
;
94 struct __filedes
*filp
,
96 int whence
///< Ignored by Console
100 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
103 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
104 // Quick check to see if Stream looks reasonable
105 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
106 EFIerrno
= RETURN_INVALID_PARAMETER
;
107 return -1; // Looks like a bad This pointer
109 if(Stream
->InstanceNum
== STDIN_FILENO
) {
110 // Seek is not valid for stdin
111 EFIerrno
= RETURN_UNSUPPORTED
;
114 // Everything is OK to do the final verification and "seek".
115 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
116 CursorPos
.Offset
= Position
;
118 EFIerrno
= Proto
->SetCursorPosition(Proto
,
119 (INTN
)CursorPos
.XYpos
.Column
,
120 (INTN
)CursorPos
.XYpos
.Row
);
122 if(RETURN_ERROR(EFIerrno
)) {
134 IN OUT
struct __filedes
*filp
,
135 IN OUT off_t
*offset
, // Console ignores this
136 IN
size_t BufferSize
,
140 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
146 EFI_STATUS Status
= RETURN_SUCCESS
;
149 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
150 // Quick check to see if Stream looks reasonable
151 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
152 EFIerrno
= RETURN_INVALID_PARAMETER
;
153 return -1; // Looks like a bad This pointer
155 if(Stream
->InstanceNum
!= STDIN_FILENO
) {
156 // Read only valid for stdin
157 EFIerrno
= RETURN_UNSUPPORTED
;
160 // It looks like things are OK for trying to read
161 // We will accumulate *BufferSize characters or until we encounter
162 // an "activation" character. Currently any control character.
163 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
165 NumChar
= (BufferSize
- 1) / sizeof(CHAR16
);
168 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
169 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
170 if(Status
!= RETURN_SUCCESS
) {
173 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
174 if(Status
!= RETURN_SUCCESS
) {
179 Key
.ScanCode
= Stream
->UnGetKey
.ScanCode
;
180 Key
.UnicodeChar
= Stream
->UnGetKey
.UnicodeChar
;
181 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
182 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
184 if(Key
.ScanCode
== SCAN_NULL
) {
185 *OutPtr
++ = Key
.UnicodeChar
;
188 if(iswcntrl(Key
.UnicodeChar
)) { // If a control character, or a scan code
191 } while(i
< NumChar
);
193 *OutPtr
= L
'\0'; // Terminate the input buffer
195 return (ssize_t
)(i
* sizeof(CHAR16
)); // Will be 0 if we didn't get a key
198 /* Write a NULL terminated WCS to the EFI console.
200 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
201 the string couldn't be displayed.
202 @param[in] Buffer The WCS string to be displayed
204 @return The number of characters written.
210 IN
struct __filedes
*filp
,
212 IN
size_t BufferSize
,
213 IN
const void *Buffer
217 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
220 //XYoffset CursorPos;
222 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
223 // Quick check to see if Stream looks reasonable
224 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
225 EFIerrno
= RETURN_INVALID_PARAMETER
;
226 return -1; // Looks like a bad This pointer
228 if(Stream
->InstanceNum
== STDIN_FILENO
) {
229 // Write is not valid for stdin
230 EFIerrno
= RETURN_UNSUPPORTED
;
233 // Everything is OK to do the write.
234 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
236 // Convert string from MBCS to WCS and translate \n to \r\n.
237 NumChar
= WideTtyCvt(gMD
->UString
, (const char *)Buffer
, BufferSize
);
239 // BufferSize = (size_t)(NumChar * sizeof(CHAR16));
241 BufferSize
= NumChar
;
243 //if( Position != NULL) {
244 // CursorPos.Offset = (UINT64)*Position;
246 // Status = Proto->SetCursorPosition(Proto,
247 // (INTN)CursorPos.XYpos.Column,
248 // (INTN)CursorPos.XYpos.Row);
249 // if(RETURN_ERROR(Status)) {
254 // Send the Unicode buffer to the console
255 Status
= Proto
->OutputString( Proto
, gMD
->UString
);
256 // Depending on status, update BufferSize and return
257 if(RETURN_ERROR(Status
)) {
258 BufferSize
= 0; // We don't really know how many characters made it out
261 //BufferSize = NumChar;
262 Stream
->NumWritten
+= NumChar
;
268 /** Console-specific helper function for the fstat() function.
270 st_size Set to number of characters read for stdin and number written for stdout and stderr.
271 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
272 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
273 st_blksize Set to 1 since this is a character device
275 All other members of the stat structure are left unchanged.
281 struct __filedes
*filp
,
287 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
294 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
295 // Quick check to see if Stream looks reasonable
296 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
299 EFIerrno
= RETURN_INVALID_PARAMETER
;
302 // All of our parameters are correct, so fill in the information.
303 Buffer
->st_blksize
= 1;
306 if(Stream
->InstanceNum
== STDIN_FILENO
) {
308 Buffer
->st_curpos
= 0;
309 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
310 Buffer
->st_physsize
= 1;
313 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
314 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
315 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
316 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
317 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
319 OutMode
= Proto
->Mode
->Mode
;
320 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
321 if(RETURN_ERROR(EFIerrno
)) {
322 Buffer
->st_physsize
= 0;
325 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
326 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
327 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
337 struct __filedes
*filp
,
345 /** Open an abstract Console Device.
350 struct __filedes
*filp
,
352 wchar_t *Path
, // Not used for console devices
353 wchar_t *Flags
// Not used for console devices
359 (DevInstance
== NULL
))
361 EFIerrno
= RETURN_INVALID_PARAMETER
;
364 Stream
= (ConInstance
*)DevInstance
;
365 // Quick check to see if Stream looks reasonable
366 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
367 EFIerrno
= RETURN_INVALID_PARAMETER
;
368 return -1; // Looks like a bad This pointer
370 gMD
->StdIo
[Stream
->InstanceNum
] = (ConInstance
*)DevInstance
;
371 filp
->f_iflags
|= (S_IFREG
| _S_IFCHR
| _S_ICONSOLE
);
373 filp
->f_ops
= &Stream
->Abstraction
;
378 #include <sys/poll.h>
379 /* Returns a bit mask describing which operations could be completed immediately.
381 (POLLIN | POLLRDNORM) A Unicode character is available to read
382 (POLLIN) A ScanCode is ready.
383 (POLLOUT) The device is ready for output - always set on stdout and stderr.
390 struct __filedes
*filp
,
394 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
396 EFI_STATUS Status
= RETURN_SUCCESS
;
399 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
400 // Quick check to see if Stream looks reasonable
401 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
402 EFIerrno
= RETURN_INVALID_PARAMETER
;
403 return POLLNVAL
; // Looks like a bad filp pointer
405 if(Stream
->InstanceNum
== 0) {
406 // Only input is supported for this device
407 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
408 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
409 Status
= Proto
->ReadKeyStroke(Proto
, &Stream
->UnGetKey
);
410 if(Status
== RETURN_SUCCESS
) {
412 if(Stream
->UnGetKey
.UnicodeChar
!= CHAR_NULL
) {
413 RdyMask
|= POLLRDNORM
;
417 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
418 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
422 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
423 // Only output is supported for this device
427 RdyMask
= POLLERR
; // Not one of the standard streams
431 return (RdyMask
& (events
| POLL_RETONLY
));
434 /** Construct the Console stream devices: stdin, stdout, stderr.
436 Allocate the instance structure and populate it with the information for
442 IN EFI_HANDLE ImageHandle
,
443 IN EFI_SYSTEM_TABLE
*SystemTable
447 RETURN_STATUS Status
= RETURN_SUCCESS
;
450 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
451 if(ConInstanceList
== NULL
) {
452 return RETURN_OUT_OF_RESOURCES
;
455 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
456 // Get pointer to instance.
457 Stream
= &ConInstanceList
[i
];
459 Stream
->Cookie
= CON_COOKIE
;
460 Stream
->InstanceNum
= i
;
464 Stream
->Dev
= SystemTable
->ConIn
;
467 Stream
->Dev
= SystemTable
->ConOut
;
470 if(SystemTable
->StdErr
== NULL
) {
471 Stream
->Dev
= SystemTable
->ConOut
;
474 Stream
->Dev
= SystemTable
->StdErr
;
478 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
481 Stream
->Abstraction
.fo_close
= &da_ConClose
;
482 Stream
->Abstraction
.fo_read
= &da_ConRead
;
483 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
484 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
485 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
486 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
487 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
488 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
489 Stream
->Abstraction
.fo_flush
= &fnullop_flush
;
490 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
491 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
492 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
493 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
496 Stream
->NumWritten
= 0;
497 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
498 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
500 if(Stream
->Dev
== NULL
) {
501 continue; // No device for this stream.
503 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
, 1, sizeof(ConInstance
), stdioFlags
[i
]);
504 if(ConNode
[i
] == NULL
) {
508 Stream
->Parent
= ConNode
[i
];
516 IN EFI_HANDLE ImageHandle
,
517 IN EFI_SYSTEM_TABLE
*SystemTable
522 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
523 if(ConNode
[i
] != NULL
) {
524 FreePool(ConNode
[i
]);
527 if(ConInstanceList
!= NULL
) {
528 FreePool(ConInstanceList
);
531 return RETURN_SUCCESS
;
534 /* ######################################################################### */
535 #if 0 /* Not implemented for Console */
541 struct __filedes
*filp
,
553 struct __filedes
*filp
558 #endif /* Not implemented for Console */