2 Abstract device driver for the UEFI Console.
4 Manipulates abstractions for stdin, stdout, stderr.
6 This device is a WIDE device and this driver returns WIDE
7 characters. It this the responsibility of the caller to convert between
8 narrow and wide characters in order to perform the desired operations.
10 The devices status as a wide device is indicatd by _S_IWTTY being set in
13 Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>
14 This program and the accompanying materials are licensed and made available under
15 the terms and conditions of the BSD License that accompanies this distribution.
16 The full text of the license may be found at
17 http://opensource.org/licenses/bsd-license.php.
19 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
20 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
24 #include <Library/BaseLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/UefiBootServicesTableLib.h>
27 #include <Protocol/SimpleTextIn.h>
28 #include <Protocol/SimpleTextOut.h>
30 #include <LibConfig.h>
36 #include <sys/fcntl.h>
39 #include <Device/Device.h>
40 #include <Device/IIO.h>
43 static const CHAR16
* const
44 stdioNames
[NUM_SPECIAL
] = {
45 L
"stdin:", L
"stdout:", L
"stderr:"
48 static const int stdioFlags
[NUM_SPECIAL
] = {
54 static DeviceNode
*ConNode
[NUM_SPECIAL
];
55 static ConInstance
*ConInstanceList
;
59 /* Flags settable by Ioctl */
60 static BOOLEAN TtyCooked
;
61 static BOOLEAN TtyEcho
;
63 /** Convert string from MBCS to WCS and translate \n to \r\n.
65 It is the caller's responsibility to ensure that dest is
66 large enough to hold the converted results. It is guaranteed
67 that there will be fewer than n characters placed in dest.
69 @param[out] dest WCS buffer to receive the converted string.
70 @param[in] buf MBCS string to convert to WCS.
71 @param[in] n Number of BYTES contained in buf.
72 @param[in,out] Cs Pointer to the character state object for this stream
74 @return The number of BYTES consumed from buf.
77 WideTtyCvt( CHAR16
*dest
, const char *buf
, ssize_t n
, mbstate_t *Cs
)
84 numB
= (int)mbrtowc(wc
, buf
, MIN(MB_LEN_MAX
,n
), Cs
);
88 if(numB
< 0) { // If an unconvertable character, replace it.
89 wc
[0] = BLOCKELEMENT_LIGHT_SHADE
;
96 *dest
++ = (CHAR16
)wc
[0];
105 /** Close an open file.
107 @param[in] filp Pointer to the file descriptor structure for this file.
109 @retval 0 The file has been successfully closed.
110 @retval -1 filp does not point to a valid console descriptor.
116 IN
struct __filedes
*filp
121 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
122 // Quick check to see if Stream looks reasonable
123 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
125 EFIerrno
= RETURN_INVALID_PARAMETER
;
126 return -1; // Looks like a bad File Descriptor pointer
128 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
132 /** Position the console cursor to the coordinates specified by Position.
134 @param[in] filp Pointer to the file descriptor structure for this file.
135 @param[in] Position A value containing the target X and Y coordinates.
136 @param[in] whence Ignored by the Console device.
138 @retval Position Success. Returns a copy of the Position argument.
139 @retval -1 filp is not associated with a valid console stream.
140 @retval -1 This console stream is attached to stdin.
141 @retval -1 The SetCursorPosition operation failed.
147 struct __filedes
*filp
,
149 int whence
///< Ignored by Console
153 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
156 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
157 // Quick check to see if Stream looks reasonable
158 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
159 EFIerrno
= RETURN_INVALID_PARAMETER
;
160 return -1; // Looks like a bad This pointer
162 if(Stream
->InstanceNum
== STDIN_FILENO
) {
163 // Seek is not valid for stdin
164 EFIerrno
= RETURN_UNSUPPORTED
;
167 // Everything is OK to do the final verification and "seek".
168 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
169 CursorPos
.Offset
= Position
;
171 EFIerrno
= Proto
->SetCursorPosition(Proto
,
172 (INTN
)CursorPos
.XYpos
.Column
,
173 (INTN
)CursorPos
.XYpos
.Row
);
175 if(RETURN_ERROR(EFIerrno
)) {
183 /* Write a NULL terminated WCS to the EFI console.
185 NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters received
186 by da_ConWrite are WIDE characters. It is the responsibility of the
187 higher-level function(s) to perform any necessary conversions.
189 @param[in,out] BufferSize Number of characters in Buffer.
190 @param[in] Buffer The WCS string to be displayed
192 @return The number of Characters written.
198 IN
struct __filedes
*filp
,
200 IN
size_t BufferSize
,
201 IN
const void *Buffer
205 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
211 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
212 // Quick check to see if Stream looks reasonable
213 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
214 EFIerrno
= RETURN_INVALID_PARAMETER
;
215 return -1; // Looks like a bad This pointer
217 if(Stream
->InstanceNum
== STDIN_FILENO
) {
218 // Write is not valid for stdin
219 EFIerrno
= RETURN_UNSUPPORTED
;
222 // Everything is OK to do the write.
223 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
225 Status
= EFI_SUCCESS
;
226 if(Position
!= NULL
) {
227 CursorPos
.Offset
= *Position
;
229 Status
= Proto
->SetCursorPosition(Proto
,
230 (INTN
)CursorPos
.XYpos
.Column
,
231 (INTN
)CursorPos
.XYpos
.Row
);
234 if(!RETURN_ERROR(Status
)) {
235 // Send the Unicode buffer to the console
236 Status
= Proto
->OutputString( Proto
, (CHAR16
*)Buffer
);
239 // Depending on status, update BufferSize and return
240 if(!RETURN_ERROR(Status
)) {
241 //BufferSize = NumChar;
242 NumChar
= BufferSize
;
243 Stream
->NumWritten
+= NumChar
;
245 EFIerrno
= Status
; // Make error reason available to caller
249 /** Read a wide character from the console input device.
251 NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters returned
252 by da_ConRead are WIDE characters. It is the responsibility of the
253 higher-level function(s) to perform any necessary conversions.
255 @param[in,out] BufferSize Number of characters in Buffer.
256 @param[in] filp Pointer to file descriptor for this file.
257 @param[in] offset Ignored.
258 @param[in] BufferSize Buffer size, in bytes.
259 @param[out] Buffer Buffer in which to place the read characters.
261 @retval -1 An error has occurred. Reason in errno and EFIerrno.
262 @retval -1 No data is available. errno is set to EAGAIN
263 @retval 1 One wide character has been placed in Buffer
269 IN OUT
struct __filedes
*filp
,
270 IN OUT off_t
*offset
, // Console ignores this
271 IN
size_t BufferSize
,
275 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
278 EFI_INPUT_KEY Key
= {0,0};
279 EFI_STATUS Status
= RETURN_SUCCESS
;
283 wchar_t RetChar
; // Default to No Data
286 if(BufferSize
< sizeof(wchar_t)) {
287 errno
= EINVAL
; // Buffer is too small to hold one character
290 Self
= (cIIO
*)filp
->devdata
;
291 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
292 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
293 Flags
= filp
->Oflags
;
294 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
295 // No data pending in the Un-get buffer. Get a char from the hardware.
296 if((Flags
& O_NONBLOCK
) == 0) {
297 // Read a byte in Blocking mode
298 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
300 if(Status
!= EFI_SUCCESS
) {
304 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
305 if(Status
== EFI_SUCCESS
) {
306 NumRead
= 1; // Indicate that Key holds the data
314 // Read a byte in Non-Blocking mode
315 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
317 if(Status
== EFI_SUCCESS
) {
319 NumRead
= 1; // Indicate that Key holds the data
321 else if(Status
== EFI_NOT_READY
) {
322 // Keystroke data is not available
332 // Use the data in the Un-get buffer
333 Key
.ScanCode
= Stream
->UnGetKey
.ScanCode
;
334 Key
.UnicodeChar
= Stream
->UnGetKey
.UnicodeChar
;
335 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
336 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
337 NumRead
= 1; // Indicate that Key holds the data
339 // If we have data, prepare it for return.
341 RetChar
= Key
.UnicodeChar
;
342 if((RetChar
== 0) && ((Self
->Termio
.c_iflag
& IGNSPEC
) == 0)) {
343 // Must be a control, function, or other non-printable key.
344 // Map it into the Platform portion of the Unicode private use area
345 RetChar
= (Key
.ScanCode
== 0) ? 0 : 0xF900U
- Key
.ScanCode
;
347 *((wchar_t *)Buffer
) = RetChar
;
353 /** Console-specific helper function for the fstat() function.
355 st_size Set to number of characters read for stdin and number written for stdout and stderr.
356 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
357 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
358 st_blksize Set to 1 since this is a character device
360 All other members of the stat structure are left unchanged.
362 @param[in] filp Pointer to file descriptor for this file.
363 @param[out] Buffer Pointer to a stat structure to receive the information.
364 @param[in,out] Something Ignored.
366 @retval 0 Successful completion.
367 @retval -1 Either filp is not associated with a console stream, or
368 Buffer is NULL. errno is set to EINVAL.
374 struct __filedes
*filp
,
380 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
387 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
388 // Quick check to see if Stream looks reasonable
389 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
393 EFIerrno
= RETURN_INVALID_PARAMETER
;
396 // All of our parameters are correct, so fill in the information.
397 Buffer
->st_blksize
= 0; // Character device, not a block device
398 Buffer
->st_mode
= filp
->f_iflags
;
401 if(Stream
->InstanceNum
== STDIN_FILENO
) {
403 Buffer
->st_curpos
= 0;
404 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
405 Buffer
->st_physsize
= 1;
408 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
409 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
410 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
411 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
412 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
414 OutMode
= Proto
->Mode
->Mode
;
415 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
416 if(RETURN_ERROR(EFIerrno
)) {
417 Buffer
->st_physsize
= 0;
420 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
421 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
422 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
428 /** Console-specific helper for the ioctl system call.
430 The console device does not directly participate in ioctl operations.
431 This function completes the device abstraction and returns an error value
432 to indicate that the function is not supported for this device.
434 @retval -1 Function is not supported for this device.
440 struct __filedes
*filp
,
449 /** Open an abstract Console Device.
451 @param[in] DevNode Pointer to the Device control structure for this stream.
452 @param[in] filp Pointer to the new file control structure for this stream.
453 @param[in] DevInstance Not used for the console device.
454 @param[in] Path Not used for the console device.
455 @param[in] MPath Not used for the console device.
457 @retval 0 This console stream has been successfully opened.
458 @retval -1 The DevNode or filp pointer is NULL.
459 @retval -1 DevNode does not point to a valid console stream device.
465 struct __filedes
*filp
,
466 int DevInstance
, // Not used for console devices
467 wchar_t *Path
, // Not used for console devices
468 wchar_t *MPath
// Not used for console devices
478 Stream
= (ConInstance
*)DevNode
->InstanceList
;
479 // Quick check to see if Stream looks reasonable
480 if(Stream
->Cookie
== CON_COOKIE
)
482 Instance
= Stream
->InstanceNum
;
483 if(Instance
< NUM_SPECIAL
) {
484 gMD
->StdIo
[Instance
] = Stream
;
485 filp
->f_iflags
|= (_S_IFCHR
| _S_ITTY
| _S_IWTTY
| _S_ICONSOLE
);
487 filp
->f_ops
= &Stream
->Abstraction
;
488 filp
->devdata
= (void *)IIO
;
494 EFIerrno
= RETURN_INVALID_PARAMETER
;
501 #include <sys/poll.h>
502 /* Returns a bit mask describing which operations could be completed immediately.
504 Testable Events for this device are:
505 (POLLIN | POLLRDNORM) A Unicode character is available to read
506 (POLLIN) A ScanCode is ready.
507 (POLLOUT) The device is ready for output - always set on stdout and stderr.
509 Non-testable Events which are only valid in return values are:
510 POLLERR The specified device is not one of stdin, stdout, or stderr.
511 POLLHUP The specified stream has been disconnected
512 POLLNVAL da_ConPoll was called with an invalid parameter.
514 NOTE: The "Events" handled by this function are not UEFI events.
516 @param[in] filp Pointer to the file control structure for this stream.
517 @param[in] events A bit mask identifying the events to be examined
520 @return Returns a bit mask comprised of both testable and non-testable
521 event codes indicating both the state of the operation and the
522 status of the device.
528 struct __filedes
*filp
,
532 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
534 EFI_STATUS Status
= RETURN_SUCCESS
;
537 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
538 // Quick check to see if Stream looks reasonable
539 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
541 EFIerrno
= RETURN_INVALID_PARAMETER
;
542 return POLLNVAL
; // Looks like a bad filp pointer
544 if(Stream
->InstanceNum
== 0) {
545 // Only input is supported for this device
546 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
547 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
548 Status
= Proto
->ReadKeyStroke(Proto
, &Stream
->UnGetKey
);
549 if(Status
== RETURN_SUCCESS
) {
551 if(Stream
->UnGetKey
.UnicodeChar
!= CHAR_NULL
) {
552 RdyMask
|= POLLRDNORM
;
556 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
557 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
561 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
562 // Only output is supported for this device
566 RdyMask
= POLLERR
; // Not one of the standard streams
570 return (RdyMask
& (events
| POLL_RETONLY
));
573 /** Construct the Console stream devices: stdin, stdout, stderr.
575 Allocate the instance structure and populate it with the information for
581 IN EFI_HANDLE ImageHandle
,
582 IN EFI_SYSTEM_TABLE
*SystemTable
586 RETURN_STATUS Status
;
589 Status
= RETURN_OUT_OF_RESOURCES
;
590 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
591 if(ConInstanceList
!= NULL
) {
594 FreePool(ConInstanceList
);
597 Status
= RETURN_SUCCESS
;
598 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
599 // Get pointer to instance.
600 Stream
= &ConInstanceList
[i
];
602 Stream
->Cookie
= CON_COOKIE
;
603 Stream
->InstanceNum
= i
;
604 Stream
->CharState
.A
= 0; // Start in the initial state
608 Stream
->Dev
= SystemTable
->ConIn
;
611 Stream
->Dev
= SystemTable
->ConOut
;
614 if(SystemTable
->StdErr
== NULL
) {
615 Stream
->Dev
= SystemTable
->ConOut
;
618 Stream
->Dev
= SystemTable
->StdErr
;
622 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
625 Stream
->Abstraction
.fo_close
= &da_ConClose
;
626 Stream
->Abstraction
.fo_read
= &da_ConRead
;
627 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
628 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
629 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
630 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
631 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
632 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
633 Stream
->Abstraction
.fo_flush
= &fnullop_flush
;
634 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
635 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
636 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
637 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
640 Stream
->NumWritten
= 0;
641 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
642 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
644 if(Stream
->Dev
== NULL
) {
645 continue; // No device for this stream.
647 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
,
648 1, sizeof(ConInstance
), stdioFlags
[i
]);
649 if(ConNode
[i
] == NULL
) {
650 Status
= EFIerrno
; // Grab error code that DevRegister produced.
653 Stream
->Parent
= ConNode
[i
];
655 /* Initialize Ioctl flags until Ioctl is really implemented. */
666 IN EFI_HANDLE ImageHandle
,
667 IN EFI_SYSTEM_TABLE
*SystemTable
672 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
673 if(ConNode
[i
] != NULL
) {
674 FreePool(ConNode
[i
]);
677 if(ConInstanceList
!= NULL
) {
678 FreePool(ConInstanceList
);
685 return RETURN_SUCCESS
;
688 /* ######################################################################### */
689 #if 0 /* Not implemented (yet?) for Console */
695 struct __filedes
*filp
,
707 struct __filedes
*filp
712 #endif /* Not implemented for Console */