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 - 2014, 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 <Library/DebugLib.h>
28 #include <Protocol/SimpleTextIn.h>
29 #include <Protocol/SimpleTextOut.h>
31 #include <LibConfig.h>
37 #include <sys/fcntl.h>
39 #include <sys/termios.h>
41 #include <Device/Device.h>
42 #include <Device/IIO.h>
45 static const CHAR16
* const
46 stdioNames
[NUM_SPECIAL
] = {
47 L
"stdin:", L
"stdout:", L
"stderr:"
50 static const int stdioFlags
[NUM_SPECIAL
] = {
56 static DeviceNode
*ConNode
[NUM_SPECIAL
];
57 static ConInstance
*ConInstanceList
;
61 /* Flags settable by Ioctl */
62 static BOOLEAN TtyCooked
;
63 static BOOLEAN TtyEcho
;
65 /** Convert string from MBCS to WCS and translate \n to \r\n.
67 It is the caller's responsibility to ensure that dest is
68 large enough to hold the converted results. It is guaranteed
69 that there will be fewer than n characters placed in dest.
71 @param[out] dest WCS buffer to receive the converted string.
72 @param[in] buf MBCS string to convert to WCS.
73 @param[in] n Number of BYTES contained in buf.
74 @param[in,out] Cs Pointer to the character state object for this stream
76 @return The number of BYTES consumed from buf.
79 WideTtyCvt( CHAR16
*dest
, const char *buf
, ssize_t n
, mbstate_t *Cs
)
86 numB
= (int)mbrtowc(wc
, buf
, MIN(MB_LEN_MAX
,n
), Cs
);
90 if(numB
< 0) { // If an unconvertable character, replace it.
91 wc
[0] = BLOCKELEMENT_LIGHT_SHADE
;
98 *dest
++ = (CHAR16
)wc
[0];
107 /** Close an open file.
109 @param[in] filp Pointer to the file descriptor structure for this file.
111 @retval 0 The file has been successfully closed.
112 @retval -1 filp does not point to a valid console descriptor.
118 IN
struct __filedes
*filp
123 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
124 // Quick check to see if Stream looks reasonable
125 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
127 EFIerrno
= RETURN_INVALID_PARAMETER
;
128 return -1; // Looks like a bad File Descriptor pointer
130 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
134 /** Position the console cursor to the coordinates specified by Position.
136 @param[in] filp Pointer to the file descriptor structure for this file.
137 @param[in] Position A value containing the target X and Y coordinates.
138 @param[in] whence Ignored by the Console device.
140 @retval Position Success. Returns a copy of the Position argument.
141 @retval -1 filp is not associated with a valid console stream.
142 @retval -1 This console stream is attached to stdin.
143 @retval -1 The SetCursorPosition operation failed.
149 struct __filedes
*filp
,
151 int whence
///< Ignored by Console
155 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
158 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
159 // Quick check to see if Stream looks reasonable
160 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
161 EFIerrno
= RETURN_INVALID_PARAMETER
;
162 return -1; // Looks like a bad This pointer
164 if(Stream
->InstanceNum
== STDIN_FILENO
) {
165 // Seek is not valid for stdin
166 EFIerrno
= RETURN_UNSUPPORTED
;
169 // Everything is OK to do the final verification and "seek".
170 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
171 CursorPos
.Offset
= Position
;
173 EFIerrno
= Proto
->SetCursorPosition(Proto
,
174 (INTN
)CursorPos
.XYpos
.Column
,
175 (INTN
)CursorPos
.XYpos
.Row
);
177 if(RETURN_ERROR(EFIerrno
)) {
185 /* Write a NULL terminated WCS to the EFI console.
187 NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters received
188 by da_ConWrite are WIDE characters. It is the responsibility of the
189 higher-level function(s) to perform any necessary conversions.
191 @param[in,out] BufferSize Number of characters in Buffer.
192 @param[in] Buffer The WCS string to be displayed
194 @return The number of Characters written.
200 IN
struct __filedes
*filp
,
202 IN
size_t BufferSize
,
203 IN
const void *Buffer
207 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
213 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
214 // Quick check to see if Stream looks reasonable
215 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
216 EFIerrno
= RETURN_INVALID_PARAMETER
;
217 return -1; // Looks like a bad This pointer
219 if(Stream
->InstanceNum
== STDIN_FILENO
) {
220 // Write is not valid for stdin
221 EFIerrno
= RETURN_UNSUPPORTED
;
224 // Everything is OK to do the write.
225 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
227 Status
= EFI_SUCCESS
;
228 if(Position
!= NULL
) {
229 CursorPos
.Offset
= *Position
;
231 Status
= Proto
->SetCursorPosition(Proto
,
232 (INTN
)CursorPos
.XYpos
.Column
,
233 (INTN
)CursorPos
.XYpos
.Row
);
236 if(!RETURN_ERROR(Status
)) {
237 // Send the Unicode buffer to the console
238 Status
= Proto
->OutputString( Proto
, (CHAR16
*)Buffer
);
241 // Depending on status, update BufferSize and return
242 if(!RETURN_ERROR(Status
)) {
243 NumChar
= BufferSize
;
244 Stream
->NumWritten
+= NumChar
;
246 EFIerrno
= Status
; // Make error reason available to caller
250 /** Read a wide character from the console input device.
252 Returns NUL or a translated input character.
254 @param[in] filp Pointer to file descriptor for this file.
255 @param[out] Buffer Buffer in which to place the read character.
257 @retval EFI_DEVICE_ERROR A hardware error has occurred.
258 @retval EFI_NOT_READY No data is available. Try again later.
259 @retval EFI_SUCCESS One wide character has been placed in Character
260 - 0x0000 NUL, ignore this
261 - Otherwise, should be a good wide character in Character
266 IN OUT
struct __filedes
*filp
,
267 OUT
wchar_t *Character
270 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
274 EFI_INPUT_KEY Key
= {0,0};
277 Self
= (cIIO
*)filp
->devdata
;
278 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
279 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
281 if(Stream
->UnGetKey
== CHAR_NULL
) {
282 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
285 Status
= EFI_SUCCESS
;
286 // Use the data in the Un-get buffer
287 // Guaranteed that ScanCode and UnicodeChar are not both NUL
288 Key
.ScanCode
= SCAN_NULL
;
289 Key
.UnicodeChar
= Stream
->UnGetKey
;
290 Stream
->UnGetKey
= CHAR_NULL
;
292 if(Status
== EFI_SUCCESS
) {
293 // Translate the Escape Scan Code to an ESC character
294 if (Key
.ScanCode
!= 0) {
295 if (Key
.ScanCode
== SCAN_ESC
) {
298 else if((Self
->Termio
.c_iflag
& IGNSPEC
) != 0) {
299 // If we are ignoring special characters, return a NUL
303 // Must be a control, function, or other non-printable key.
304 // Map it into the Platform portion of the Unicode private use area
305 RetChar
= TtyFunKeyMax
- Key
.ScanCode
;
309 RetChar
= Key
.UnicodeChar
;
311 *Character
= RetChar
;
319 /** Read a wide character from the console input device.
321 NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters returned
322 by da_ConRead are WIDE characters. It is the responsibility of the
323 higher-level function(s) to perform any necessary conversions.
325 A NUL character, 0x0000, is never returned. In the event that such a character
326 is encountered, the read is either retried or -1 is returned with errno set
329 @param[in] filp Pointer to file descriptor for this file.
330 @param[in] offset Ignored.
331 @param[in] BufferSize Buffer size, in bytes.
332 @param[out] Buffer Buffer in which to place the read characters.
334 @retval -1 An error has occurred. Reason in errno and EFIerrno.
335 @retval -1 No data is available. errno is set to EAGAIN
336 @retval 1 One wide character has been placed in Buffer
342 IN OUT
struct __filedes
*filp
,
343 IN OUT off_t
*offset
, // Console ignores this
344 IN
size_t BufferSize
,
348 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
354 BOOLEAN BlockingMode
;
358 if(BufferSize
< sizeof(wchar_t)) {
359 errno
= EINVAL
; // Buffer is too small to hold one character
362 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
363 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
364 BlockingMode
= ((filp
->Oflags
& O_NONBLOCK
) == 0);
367 Status
= EFI_SUCCESS
;
369 // Read a byte in Blocking mode
370 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
373 /* WaitForEvent should not be able to fail since
374 NumberOfEvents is set to constant 1 so is never 0
375 Event is set by the Simple Text Input protocol so should never be EVT_NOTIFY_SIGNAL
376 Current TPL should be TPL_APPLICATION.
377 ASSERT so that we catch any problems during development.
379 ASSERT(Status
== EFI_SUCCESS
);
381 Status
= da_ConRawRead (filp
, &RetChar
);
382 } while ( BlockingMode
&&
384 (Status
!= EFI_DEVICE_ERROR
));
387 if(Status
== EFI_SUCCESS
) {
389 NumRead
= 1; // Indicate that Key holds the data
391 else if(Status
== EFI_NOT_READY
) {
392 // Keystroke data is not available
404 *((wchar_t *)Buffer
) = RetChar
;
410 /** Console-specific helper function for the fstat() function.
412 st_size Set to number of characters read for stdin and number written for stdout and stderr.
413 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
414 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
415 st_blksize Set to 1 since this is a character device
417 All other members of the stat structure are left unchanged.
419 @param[in] filp Pointer to file descriptor for this file.
420 @param[out] Buffer Pointer to a stat structure to receive the information.
421 @param[in,out] Something Ignored.
423 @retval 0 Successful completion.
424 @retval -1 Either filp is not associated with a console stream, or
425 Buffer is NULL. errno is set to EINVAL.
431 struct __filedes
*filp
,
437 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
444 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
445 // Quick check to see if Stream looks reasonable
446 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
450 EFIerrno
= RETURN_INVALID_PARAMETER
;
453 // All of our parameters are correct, so fill in the information.
454 Buffer
->st_blksize
= 0; // Character device, not a block device
455 Buffer
->st_mode
= filp
->f_iflags
;
458 if(Stream
->InstanceNum
== STDIN_FILENO
) {
460 Buffer
->st_curpos
= 0;
461 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
462 Buffer
->st_physsize
= 1;
465 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
466 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
467 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
468 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
469 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
471 OutMode
= Proto
->Mode
->Mode
;
472 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
473 if(RETURN_ERROR(EFIerrno
)) {
474 Buffer
->st_physsize
= 0;
477 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
478 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
479 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
485 /** Console-specific helper for the ioctl system call.
487 The console device does not directly participate in ioctl operations.
488 This function completes the device abstraction and returns an error value
489 to indicate that the function is not supported for this device.
491 @retval -1 Function is not supported for this device.
497 struct __filedes
*filp
,
506 /** Open an abstract Console Device.
508 @param[in] DevNode Pointer to the Device control structure for this stream.
509 @param[in] filp Pointer to the new file control structure for this stream.
510 @param[in] DevInstance Not used for the console device.
511 @param[in] Path Not used for the console device.
512 @param[in] MPath Not used for the console device.
514 @retval 0 This console stream has been successfully opened.
515 @retval -1 The DevNode or filp pointer is NULL.
516 @retval -1 DevNode does not point to a valid console stream device.
522 struct __filedes
*filp
,
523 int DevInstance
, // Not used for console devices
524 wchar_t *Path
, // Not used for console devices
525 wchar_t *MPath
// Not used for console devices
535 Stream
= (ConInstance
*)DevNode
->InstanceList
;
536 // Quick check to see if Stream looks reasonable
537 if(Stream
->Cookie
== CON_COOKIE
)
539 Instance
= Stream
->InstanceNum
;
540 if(Instance
< NUM_SPECIAL
) {
541 gMD
->StdIo
[Instance
] = Stream
;
542 filp
->f_iflags
|= (_S_IFCHR
| _S_ITTY
| _S_IWTTY
| _S_ICONSOLE
);
544 filp
->f_ops
= &Stream
->Abstraction
;
545 filp
->devdata
= (void *)IIO
;
551 EFIerrno
= RETURN_INVALID_PARAMETER
;
558 #include <sys/poll.h>
559 /* Returns a bit mask describing which operations could be completed immediately.
561 Testable Events for this device are:
562 (POLLIN | POLLRDNORM) A Unicode character is available to read
563 (POLLIN) A ScanCode is ready.
564 (POLLOUT) The device is ready for output - always set on stdout and stderr.
566 Non-testable Events which are only valid in return values are:
567 POLLERR The specified device is not one of stdin, stdout, or stderr.
568 POLLHUP The specified stream has been disconnected
569 POLLNVAL da_ConPoll was called with an invalid parameter.
571 NOTE: The "Events" handled by this function are not UEFI events.
573 @param[in] filp Pointer to the file control structure for this stream.
574 @param[in] events A bit mask identifying the events to be examined
577 @return Returns a bit mask comprised of both testable and non-testable
578 event codes indicating both the state of the operation and the
579 status of the device.
585 struct __filedes
*filp
,
589 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
591 EFI_STATUS Status
= RETURN_SUCCESS
;
594 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
595 // Quick check to see if Stream looks reasonable
596 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
598 EFIerrno
= RETURN_INVALID_PARAMETER
;
599 return POLLNVAL
; // Looks like a bad filp pointer
601 if(Stream
->InstanceNum
== 0) {
602 // STDIN: Only input is supported for this device
603 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
604 Status
= da_ConRawRead (filp
, &Stream
->UnGetKey
);
605 if(Status
== RETURN_SUCCESS
) {
607 if ((Stream
->UnGetKey
< TtyFunKeyMin
) ||
608 (Stream
->UnGetKey
>= TtyFunKeyMax
))
610 RdyMask
|= POLLRDNORM
;
614 Stream
->UnGetKey
= CHAR_NULL
;
617 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
618 // (STDOUT || STDERR): Only output is supported for this device
622 RdyMask
= POLLERR
; // Not one of the standard streams
626 return (RdyMask
& (events
| POLL_RETONLY
));
629 /** Construct the Console stream devices: stdin, stdout, stderr.
631 Allocate the instance structure and populate it with the information for
637 IN EFI_HANDLE ImageHandle
,
638 IN EFI_SYSTEM_TABLE
*SystemTable
642 RETURN_STATUS Status
;
645 Status
= RETURN_OUT_OF_RESOURCES
;
646 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
647 if(ConInstanceList
!= NULL
) {
650 FreePool(ConInstanceList
);
653 Status
= RETURN_SUCCESS
;
654 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
655 // Get pointer to instance.
656 Stream
= &ConInstanceList
[i
];
658 Stream
->Cookie
= CON_COOKIE
;
659 Stream
->InstanceNum
= i
;
660 Stream
->CharState
.A
= 0; // Start in the initial state
664 Stream
->Dev
= SystemTable
->ConIn
;
667 Stream
->Dev
= SystemTable
->ConOut
;
670 if(SystemTable
->StdErr
== NULL
) {
671 Stream
->Dev
= SystemTable
->ConOut
;
674 Stream
->Dev
= SystemTable
->StdErr
;
678 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
681 Stream
->Abstraction
.fo_close
= &da_ConClose
;
682 Stream
->Abstraction
.fo_read
= &da_ConRead
;
683 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
684 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
685 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
686 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
687 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
688 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
689 Stream
->Abstraction
.fo_flush
= &fnullop_flush
;
690 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
691 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
692 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
693 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
696 Stream
->NumWritten
= 0;
697 Stream
->UnGetKey
= CHAR_NULL
;
699 if(Stream
->Dev
== NULL
) {
700 continue; // No device for this stream.
702 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
,
703 1, sizeof(ConInstance
), stdioFlags
[i
]);
704 if(ConNode
[i
] == NULL
) {
705 Status
= EFIerrno
; // Grab error code that DevRegister produced.
708 Stream
->Parent
= ConNode
[i
];
710 /* Initialize Ioctl flags until Ioctl is really implemented. */
721 IN EFI_HANDLE ImageHandle
,
722 IN EFI_SYSTEM_TABLE
*SystemTable
727 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
728 if(ConNode
[i
] != NULL
) {
729 FreePool(ConNode
[i
]);
732 if(ConInstanceList
!= NULL
) {
733 FreePool(ConInstanceList
);
740 return RETURN_SUCCESS
;
743 /* ######################################################################### */
744 #if 0 /* Not implemented (yet?) for Console */
750 struct __filedes
*filp
,
762 struct __filedes
*filp
767 #endif /* Not implemented for Console */