mirror of https://github.com/acidanthera/audk.git
826 lines
26 KiB
C
826 lines
26 KiB
C
/** @file
|
|
Abstract device driver for the UEFI Console.
|
|
|
|
Manipulates abstractions for stdin, stdout, stderr.
|
|
|
|
This device is a WIDE device and this driver returns WIDE
|
|
characters. It this the responsibility of the caller to convert between
|
|
narrow and wide characters in order to perform the desired operations.
|
|
|
|
The devices status as a wide device is indicatd by _S_IWTTY being set in
|
|
f_iflags.
|
|
|
|
Copyright (c) 2016, Daryl McDaniel. All rights reserved.<BR>
|
|
Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials are licensed and made available under
|
|
the terms and conditions of the BSD License that accompanies this distribution.
|
|
The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
#include <Uefi.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Protocol/SimpleTextIn.h>
|
|
#include <Protocol/SimpleTextOut.h>
|
|
|
|
#include <LibConfig.h>
|
|
|
|
#include <errno.h>
|
|
#include <wctype.h>
|
|
#include <wchar.h>
|
|
#include <stdarg.h>
|
|
#include <sys/fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/termios.h>
|
|
#include <Efi/SysEfi.h>
|
|
#include <kfile.h>
|
|
#include <Device/Device.h>
|
|
#include <Device/IIO.h>
|
|
#include <MainData.h>
|
|
|
|
static const CHAR16* const
|
|
stdioNames[NUM_SPECIAL] = {
|
|
L"stdin:", L"stdout:", L"stderr:"
|
|
};
|
|
|
|
static const int stdioFlags[NUM_SPECIAL] = {
|
|
O_RDONLY, // stdin
|
|
O_WRONLY, // stdout
|
|
O_WRONLY // stderr
|
|
};
|
|
|
|
static DeviceNode *ConNode[NUM_SPECIAL];
|
|
static ConInstance *ConInstanceList;
|
|
|
|
static cIIO *IIO;
|
|
|
|
/* Flags settable by Ioctl */
|
|
static BOOLEAN TtyCooked;
|
|
static BOOLEAN TtyEcho;
|
|
|
|
/** Convert string from MBCS to WCS and translate \n to \r\n.
|
|
|
|
It is the caller's responsibility to ensure that dest is
|
|
large enough to hold the converted results. It is guaranteed
|
|
that there will be fewer than n characters placed in dest.
|
|
|
|
@param[out] dest WCS buffer to receive the converted string.
|
|
@param[in] buf MBCS string to convert to WCS.
|
|
@param[in] n Number of BYTES contained in buf.
|
|
@param[in,out] Cs Pointer to the character state object for this stream
|
|
|
|
@return The number of BYTES consumed from buf.
|
|
**/
|
|
ssize_t
|
|
WideTtyCvt( CHAR16 *dest, const char *buf, ssize_t n, mbstate_t *Cs)
|
|
{
|
|
ssize_t i = 0;
|
|
int numB = 0;
|
|
wchar_t wc[2];
|
|
|
|
while(n > 0) {
|
|
numB = (int)mbrtowc(wc, buf, MIN(MB_LEN_MAX,n), Cs);
|
|
if( numB == 0) {
|
|
break;
|
|
};
|
|
if(numB < 0) { // If an unconvertable character, replace it.
|
|
wc[0] = BLOCKELEMENT_LIGHT_SHADE;
|
|
numB = 1;
|
|
}
|
|
if(wc[0] == L'\n') {
|
|
*dest++ = L'\r';
|
|
++i;
|
|
}
|
|
*dest++ = (CHAR16)wc[0];
|
|
i += numB;
|
|
n -= numB;
|
|
buf += numB;
|
|
}
|
|
*dest = 0;
|
|
return i;
|
|
}
|
|
|
|
/** Position the console cursor to the coordinates specified by Position.
|
|
|
|
@param[in] filp Pointer to the file descriptor structure for this file.
|
|
@param[in] Position A value containing the target X and Y coordinates.
|
|
@param[in] whence Ignored by the Console device.
|
|
|
|
@retval Position Success. Returns a copy of the Position argument.
|
|
@retval -1 filp is not associated with a valid console stream.
|
|
@retval -1 This console stream is attached to stdin.
|
|
@retval -1 The SetCursorPosition operation failed.
|
|
**/
|
|
static
|
|
off_t
|
|
EFIAPI
|
|
da_ConSeek(
|
|
struct __filedes *filp,
|
|
off_t Position,
|
|
int whence ///< Ignored by Console
|
|
)
|
|
{
|
|
ConInstance *Stream;
|
|
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
|
|
XY_OFFSET CursorPos;
|
|
|
|
Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
|
|
// Quick check to see if Stream looks reasonable
|
|
if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
|
|
EFIerrno = RETURN_INVALID_PARAMETER;
|
|
return -1; // Looks like a bad This pointer
|
|
}
|
|
if(Stream->InstanceNum == STDIN_FILENO) {
|
|
// Seek is not valid for stdin
|
|
EFIerrno = RETURN_UNSUPPORTED;
|
|
return -1;
|
|
}
|
|
// Everything is OK to do the final verification and "seek".
|
|
Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
|
|
CursorPos.Offset = Position;
|
|
|
|
EFIerrno = Proto->SetCursorPosition(Proto,
|
|
(INTN)CursorPos.XYpos.Column,
|
|
(INTN)CursorPos.XYpos.Row);
|
|
|
|
if(RETURN_ERROR(EFIerrno)) {
|
|
return -1;
|
|
}
|
|
else {
|
|
return Position;
|
|
}
|
|
}
|
|
|
|
/* Write a NULL terminated WCS to the EFI console.
|
|
|
|
NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters received
|
|
by da_ConWrite are WIDE characters. It is the responsibility of the
|
|
higher-level function(s) to perform any necessary conversions.
|
|
|
|
@param[in,out] BufferSize Number of characters in Buffer.
|
|
@param[in] Buffer The WCS string to be displayed
|
|
|
|
@return The number of Characters written.
|
|
*/
|
|
static
|
|
ssize_t
|
|
EFIAPI
|
|
da_ConWrite(
|
|
IN struct __filedes *filp,
|
|
IN off_t *Position,
|
|
IN size_t BufferSize,
|
|
IN const void *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
|
|
ConInstance *Stream;
|
|
ssize_t NumChar;
|
|
XY_OFFSET CursorPos;
|
|
|
|
NumChar = -1;
|
|
Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
|
|
// Quick check to see if Stream looks reasonable
|
|
if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
|
|
EFIerrno = RETURN_INVALID_PARAMETER;
|
|
return -1; // Looks like a bad This pointer
|
|
}
|
|
if(Stream->InstanceNum == STDIN_FILENO) {
|
|
// Write is not valid for stdin
|
|
EFIerrno = RETURN_UNSUPPORTED;
|
|
return -1;
|
|
}
|
|
// Everything is OK to do the write.
|
|
Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
|
|
|
|
Status = EFI_SUCCESS;
|
|
if(Position != NULL) {
|
|
CursorPos.Offset = *Position;
|
|
|
|
Status = Proto->SetCursorPosition(Proto,
|
|
(INTN)CursorPos.XYpos.Column,
|
|
(INTN)CursorPos.XYpos.Row);
|
|
|
|
}
|
|
if(!RETURN_ERROR(Status)) {
|
|
// Send the Unicode buffer to the console
|
|
Status = Proto->OutputString( Proto, (CHAR16 *)Buffer);
|
|
}
|
|
|
|
// Depending on status, update BufferSize and return
|
|
if(!RETURN_ERROR(Status)) {
|
|
NumChar = BufferSize;
|
|
Stream->NumWritten += NumChar;
|
|
}
|
|
EFIerrno = Status; // Make error reason available to caller
|
|
return NumChar;
|
|
}
|
|
|
|
/** Read a wide character from the console input device.
|
|
|
|
Returns NUL or a translated input character.
|
|
|
|
@param[in] filp Pointer to file descriptor for this file.
|
|
@param[out] Buffer Buffer in which to place the read character.
|
|
|
|
@retval EFI_DEVICE_ERROR A hardware error has occurred.
|
|
@retval EFI_NOT_READY No data is available. Try again later.
|
|
@retval EFI_SUCCESS One wide character has been placed in Character
|
|
- 0x0000 NUL, ignore this
|
|
- Otherwise, should be a good wide character in Character
|
|
**/
|
|
static
|
|
EFI_STATUS
|
|
da_ConRawRead (
|
|
IN OUT struct __filedes *filp,
|
|
OUT wchar_t *Character
|
|
)
|
|
{
|
|
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
|
|
ConInstance *Stream;
|
|
cIIO *Self;
|
|
EFI_STATUS Status;
|
|
EFI_INPUT_KEY Key = {0,0};
|
|
wchar_t RetChar;
|
|
|
|
Self = (cIIO *)filp->devdata;
|
|
Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
|
|
Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
|
|
|
|
if(Stream->UnGetKey == CHAR_NULL) {
|
|
Status = Proto->ReadKeyStroke(Proto, &Key);
|
|
}
|
|
else {
|
|
Status = EFI_SUCCESS;
|
|
// Use the data in the Un-get buffer
|
|
// Guaranteed that ScanCode and UnicodeChar are not both NUL
|
|
Key.ScanCode = SCAN_NULL;
|
|
Key.UnicodeChar = Stream->UnGetKey;
|
|
Stream->UnGetKey = CHAR_NULL;
|
|
}
|
|
if(Status == EFI_SUCCESS) {
|
|
// Translate the Escape Scan Code to an ESC character
|
|
if (Key.ScanCode != 0) {
|
|
if (Key.ScanCode == SCAN_ESC) {
|
|
RetChar = CHAR_ESC;
|
|
}
|
|
else if((Self->Termio.c_iflag & IGNSPEC) != 0) {
|
|
// If we are ignoring special characters, return a NUL
|
|
RetChar = 0;
|
|
}
|
|
else {
|
|
// Must be a control, function, or other non-printable key.
|
|
// Map it into the Platform portion of the Unicode private use area
|
|
RetChar = TtyFunKeyMax - Key.ScanCode;
|
|
}
|
|
}
|
|
else {
|
|
RetChar = Key.UnicodeChar;
|
|
}
|
|
*Character = RetChar;
|
|
}
|
|
else {
|
|
*Character = 0;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/** Read a wide character from the console input device.
|
|
|
|
NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters returned
|
|
by da_ConRead are WIDE characters. It is the responsibility of the
|
|
higher-level function(s) to perform any necessary conversions.
|
|
|
|
A NUL character, 0x0000, is never returned. In the event that such a character
|
|
is encountered, the read is either retried or -1 is returned with errno set
|
|
to EAGAIN.
|
|
|
|
@param[in] filp Pointer to file descriptor for this file.
|
|
@param[in] offset Ignored.
|
|
@param[in] BufferSize Buffer size, in bytes.
|
|
@param[out] Buffer Buffer in which to place the read characters.
|
|
|
|
@retval -1 An error has occurred. Reason in errno and EFIerrno.
|
|
@retval -1 No data is available. errno is set to EAGAIN
|
|
@retval 1 One wide character has been placed in Buffer
|
|
**/
|
|
static
|
|
ssize_t
|
|
EFIAPI
|
|
da_ConRead(
|
|
IN OUT struct __filedes *filp,
|
|
IN OUT off_t *offset, // Console ignores this
|
|
IN size_t BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
|
|
ConInstance *Stream;
|
|
//cIIO *Self;
|
|
EFI_STATUS Status;
|
|
UINTN Edex;
|
|
ssize_t NumRead;
|
|
BOOLEAN BlockingMode;
|
|
wchar_t RetChar;
|
|
|
|
NumRead = -1;
|
|
if(BufferSize < sizeof(wchar_t)) {
|
|
errno = EINVAL; // Buffer is too small to hold one character
|
|
}
|
|
else {
|
|
Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
|
|
Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
|
|
BlockingMode = (BOOLEAN)((filp->Oflags & O_NONBLOCK) == 0);
|
|
|
|
do {
|
|
Status = EFI_SUCCESS;
|
|
if(BlockingMode) {
|
|
// Read a byte in Blocking mode
|
|
Status = gBS->WaitForEvent( 1, &Proto->WaitForKey, &Edex);
|
|
}
|
|
|
|
/* WaitForEvent should not be able to fail since
|
|
NumberOfEvents is set to constant 1 so is never 0
|
|
Event is set by the Simple Text Input protocol so should never be EVT_NOTIFY_SIGNAL
|
|
Current TPL should be TPL_APPLICATION.
|
|
ASSERT so that we catch any problems during development.
|
|
*/
|
|
ASSERT(Status == EFI_SUCCESS);
|
|
|
|
Status = da_ConRawRead (filp, &RetChar);
|
|
} while ( BlockingMode &&
|
|
(RetChar == 0) &&
|
|
(Status != EFI_DEVICE_ERROR));
|
|
|
|
EFIerrno = Status;
|
|
if(Status == EFI_SUCCESS) {
|
|
// Got a keystroke.
|
|
NumRead = 1; // Indicate that Key holds the data
|
|
}
|
|
else if(Status == EFI_NOT_READY) {
|
|
// Keystroke data is not available
|
|
errno = EAGAIN;
|
|
}
|
|
else {
|
|
// Hardware error
|
|
errno = EIO;
|
|
}
|
|
if (RetChar == 0) {
|
|
NumRead = -1;
|
|
errno = EAGAIN;
|
|
}
|
|
else {
|
|
*((wchar_t *)Buffer) = RetChar;
|
|
}
|
|
}
|
|
return NumRead;
|
|
}
|
|
|
|
/** Console-specific helper function for the fstat() function.
|
|
|
|
st_size Set to number of characters read for stdin and number written for stdout and stderr.
|
|
st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
|
|
st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
|
|
st_blksize Set to 1 since this is a character device
|
|
|
|
All other members of the stat structure are left unchanged.
|
|
|
|
@param[in] filp Pointer to file descriptor for this file.
|
|
@param[out] Buffer Pointer to a stat structure to receive the information.
|
|
@param[in,out] Something Ignored.
|
|
|
|
@retval 0 Successful completion.
|
|
@retval -1 Either filp is not associated with a console stream, or
|
|
Buffer is NULL. errno is set to EINVAL.
|
|
**/
|
|
static
|
|
int
|
|
EFIAPI
|
|
da_ConStat(
|
|
struct __filedes *filp,
|
|
struct stat *Buffer,
|
|
void *Something
|
|
)
|
|
{
|
|
ConInstance *Stream;
|
|
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
|
|
XY_OFFSET CursorPos;
|
|
INT32 OutMode;
|
|
UINTN ModeCol;
|
|
UINTN ModeRow;
|
|
|
|
// ConGetInfo
|
|
Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
|
|
// Quick check to see if Stream looks reasonable
|
|
if ((Stream->Cookie != CON_COOKIE) || // Cookie == 'IoAb'
|
|
(Buffer == NULL))
|
|
{
|
|
errno = EINVAL;
|
|
EFIerrno = RETURN_INVALID_PARAMETER;
|
|
return -1;
|
|
}
|
|
// All of our parameters are correct, so fill in the information.
|
|
Buffer->st_blksize = 0; // Character device, not a block device
|
|
Buffer->st_mode = filp->f_iflags;
|
|
|
|
// ConGetPosition
|
|
if(Stream->InstanceNum == STDIN_FILENO) {
|
|
// This is stdin
|
|
Buffer->st_curpos = 0;
|
|
Buffer->st_size = (off_t)Stream->NumRead;
|
|
Buffer->st_physsize = 1;
|
|
}
|
|
else {
|
|
Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
|
|
CursorPos.XYpos.Column = (UINT32)Proto->Mode->CursorColumn;
|
|
CursorPos.XYpos.Row = (UINT32)Proto->Mode->CursorRow;
|
|
Buffer->st_curpos = (off_t)CursorPos.Offset;
|
|
Buffer->st_size = (off_t)Stream->NumWritten;
|
|
|
|
OutMode = Proto->Mode->Mode;
|
|
EFIerrno = Proto->QueryMode(Proto, (UINTN)OutMode, &ModeCol, &ModeRow);
|
|
if(RETURN_ERROR(EFIerrno)) {
|
|
Buffer->st_physsize = 0;
|
|
}
|
|
else {
|
|
CursorPos.XYpos.Column = (UINT32)ModeCol;
|
|
CursorPos.XYpos.Row = (UINT32)ModeRow;
|
|
Buffer->st_physsize = (off_t)CursorPos.Offset;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Console-specific helper for the ioctl system call.
|
|
|
|
The console device does not directly participate in ioctl operations.
|
|
This function completes the device abstraction and returns an error value
|
|
to indicate that the function is not supported for this device.
|
|
|
|
@retval -1 Function is not supported for this device.
|
|
**/
|
|
static
|
|
int
|
|
EFIAPI
|
|
da_ConIoctl(
|
|
struct __filedes *filp,
|
|
ULONGN cmd,
|
|
va_list argp
|
|
)
|
|
{
|
|
errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
/** Open an abstract Console Device.
|
|
|
|
@param[in] DevNode Pointer to the Device control structure for this stream.
|
|
@param[in] filp Pointer to the new file control structure for this stream.
|
|
@param[in] DevInstance Not used for the console device.
|
|
@param[in] Path Not used for the console device.
|
|
@param[in] MPath Not used for the console device.
|
|
|
|
@retval 0 This console stream has been successfully opened.
|
|
@retval -1 The DevNode or filp pointer is NULL.
|
|
@retval -1 DevNode does not point to a valid console stream device.
|
|
**/
|
|
int
|
|
EFIAPI
|
|
da_ConOpen(
|
|
DeviceNode *DevNode,
|
|
struct __filedes *filp,
|
|
int DevInstance, // Not used for console devices
|
|
wchar_t *Path, // Not used for console devices
|
|
wchar_t *MPath // Not used for console devices
|
|
)
|
|
{
|
|
ConInstance *Stream;
|
|
UINT32 Instance;
|
|
int RetVal = -1;
|
|
|
|
if((filp != NULL) &&
|
|
(DevNode != NULL))
|
|
{
|
|
Stream = (ConInstance *)DevNode->InstanceList;
|
|
// Quick check to see if Stream looks reasonable
|
|
if(Stream->Cookie == CON_COOKIE)
|
|
{
|
|
Instance = Stream->InstanceNum;
|
|
if(Instance < NUM_SPECIAL) {
|
|
gMD->StdIo[Instance] = Stream;
|
|
filp->f_iflags |= (_S_IFCHR | _S_ITTY | _S_IWTTY | _S_ICONSOLE);
|
|
filp->f_offset = 0;
|
|
filp->f_ops = &Stream->Abstraction;
|
|
filp->devdata = (void *)IIO;
|
|
RetVal = 0;
|
|
}
|
|
}
|
|
}
|
|
if (RetVal < 0) {
|
|
EFIerrno = RETURN_INVALID_PARAMETER;
|
|
errno = EINVAL;
|
|
}
|
|
return RetVal;
|
|
|
|
}
|
|
|
|
/** Flush a console device's IIO buffers.
|
|
|
|
Flush the IIO Input or Output buffers associated with the specified file.
|
|
|
|
If the console is open for output, write any unwritten data in the associated
|
|
output buffer (stdout or stderr) to the console.
|
|
|
|
If the console is open for input, discard any remaining data
|
|
in the input buffer.
|
|
|
|
@param[in] filp Pointer to the target file's descriptor structure.
|
|
|
|
@retval 0 Always succeeds
|
|
**/
|
|
static
|
|
int
|
|
EFIAPI
|
|
da_ConFlush(
|
|
struct __filedes *filp
|
|
)
|
|
{
|
|
cFIFO *OutBuf;
|
|
ssize_t NumProc;
|
|
int Flags;
|
|
|
|
|
|
if(filp->MyFD == STDERR_FILENO) {
|
|
OutBuf = IIO->ErrBuf;
|
|
}
|
|
else {
|
|
OutBuf = IIO->OutBuf;
|
|
}
|
|
|
|
Flags = filp->Oflags & O_ACCMODE; // Get the device's open mode
|
|
if (Flags != O_WRONLY) { // (Flags == O_RDONLY) || (Flags == O_RDWR)
|
|
// Readable so discard the contents of the input buffer
|
|
IIO->InBuf->Flush(IIO->InBuf, UNICODE_STRING_MAX);
|
|
}
|
|
if (Flags != O_RDONLY) { // (Flags == O_WRONLY) || (Flags == O_RDWR)
|
|
// Writable so flush the output buffer
|
|
// At this point, the characters to write are in OutBuf
|
|
// First, linearize and consume the buffer
|
|
NumProc = OutBuf->Read(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
|
|
if (NumProc > 0) { // Optimization -- Nothing to do if no characters
|
|
gMD->UString[NumProc] = 0; // Ensure that the buffer is terminated
|
|
|
|
/* OutBuf always contains wide characters.
|
|
The UEFI Console (this device) always expects wide characters.
|
|
There is no need to handle devices that expect narrow characters
|
|
like the device-independent functions do.
|
|
*/
|
|
// Do the actual write of the data to the console
|
|
(void) da_ConWrite(filp, NULL, NumProc, gMD->UString);
|
|
// Paranoia -- Make absolutely sure that OutBuf is empty in case fo_write
|
|
// wasn't able to consume everything.
|
|
OutBuf->Flush(OutBuf, UNICODE_STRING_MAX);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Close an open file.
|
|
|
|
@param[in] filp Pointer to the file descriptor structure for this file.
|
|
|
|
@retval 0 The file has been successfully closed.
|
|
@retval -1 filp does not point to a valid console descriptor.
|
|
**/
|
|
static
|
|
int
|
|
EFIAPI
|
|
da_ConClose(
|
|
IN struct __filedes *filp
|
|
)
|
|
{
|
|
ConInstance *Stream;
|
|
|
|
Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
|
|
// Quick check to see if Stream looks reasonable
|
|
if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
|
|
errno = EINVAL;
|
|
EFIerrno = RETURN_INVALID_PARAMETER;
|
|
return -1; // Looks like a bad File Descriptor pointer
|
|
}
|
|
// Stream and filp look OK, so continue.
|
|
// Flush the I/O buffers
|
|
(void) da_ConFlush(filp);
|
|
|
|
// Break the connection to IIO
|
|
filp->devdata = NULL;
|
|
|
|
gMD->StdIo[Stream->InstanceNum] = NULL; // Mark the stream as closed
|
|
return 0;
|
|
}
|
|
|
|
#include <sys/poll.h>
|
|
/* Returns a bit mask describing which operations could be completed immediately.
|
|
|
|
Testable Events for this device are:
|
|
(POLLIN | POLLRDNORM) A Unicode character is available to read
|
|
(POLLIN) A ScanCode is ready.
|
|
(POLLOUT) The device is ready for output - always set on stdout and stderr.
|
|
|
|
Non-testable Events which are only valid in return values are:
|
|
POLLERR The specified device is not one of stdin, stdout, or stderr.
|
|
POLLHUP The specified stream has been disconnected
|
|
POLLNVAL da_ConPoll was called with an invalid parameter.
|
|
|
|
NOTE: The "Events" handled by this function are not UEFI events.
|
|
|
|
@param[in] filp Pointer to the file control structure for this stream.
|
|
@param[in] events A bit mask identifying the events to be examined
|
|
for this device.
|
|
|
|
@return Returns a bit mask comprised of both testable and non-testable
|
|
event codes indicating both the state of the operation and the
|
|
status of the device.
|
|
*/
|
|
static
|
|
short
|
|
EFIAPI
|
|
da_ConPoll(
|
|
struct __filedes *filp,
|
|
short events
|
|
)
|
|
{
|
|
ConInstance *Stream;
|
|
EFI_STATUS Status = RETURN_SUCCESS;
|
|
short RdyMask = 0;
|
|
|
|
Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
|
|
// Quick check to see if Stream looks reasonable
|
|
if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
|
|
errno = EINVAL;
|
|
EFIerrno = RETURN_INVALID_PARAMETER;
|
|
return POLLNVAL; // Looks like a bad filp pointer
|
|
}
|
|
if(Stream->InstanceNum == 0) {
|
|
// STDIN: Only input is supported for this device
|
|
Status = da_ConRawRead (filp, &Stream->UnGetKey);
|
|
if(Status == RETURN_SUCCESS) {
|
|
RdyMask = POLLIN;
|
|
if ((Stream->UnGetKey < TtyFunKeyMin) ||
|
|
(Stream->UnGetKey >= TtyFunKeyMax))
|
|
{
|
|
RdyMask |= POLLRDNORM;
|
|
}
|
|
}
|
|
else {
|
|
Stream->UnGetKey = CHAR_NULL;
|
|
}
|
|
}
|
|
else if(Stream->InstanceNum < NUM_SPECIAL) { // Not 0, is it 1 or 2?
|
|
// (STDOUT || STDERR): Only output is supported for this device
|
|
RdyMask = POLLOUT;
|
|
}
|
|
else {
|
|
RdyMask = POLLERR; // Not one of the standard streams
|
|
}
|
|
EFIerrno = Status;
|
|
|
|
return (RdyMask & (events | POLL_RETONLY));
|
|
}
|
|
|
|
/** Construct the Console stream devices: stdin, stdout, stderr.
|
|
|
|
Allocate the instance structure and populate it with the information for
|
|
each stream device.
|
|
**/
|
|
RETURN_STATUS
|
|
EFIAPI
|
|
__Cons_construct(
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
ConInstance *Stream;
|
|
RETURN_STATUS Status;
|
|
int i;
|
|
|
|
Status = RETURN_OUT_OF_RESOURCES;
|
|
ConInstanceList = (ConInstance *)AllocateZeroPool(NUM_SPECIAL * sizeof(ConInstance));
|
|
if(ConInstanceList != NULL) {
|
|
IIO = New_cIIO();
|
|
if(IIO == NULL) {
|
|
FreePool(ConInstanceList);
|
|
}
|
|
else {
|
|
Status = RETURN_SUCCESS;
|
|
for( i = 0; i < NUM_SPECIAL; ++i) {
|
|
// Get pointer to instance.
|
|
Stream = &ConInstanceList[i];
|
|
|
|
Stream->Cookie = CON_COOKIE;
|
|
Stream->InstanceNum = i;
|
|
Stream->CharState.A = 0; // Start in the initial state
|
|
|
|
switch(i) {
|
|
case STDIN_FILENO:
|
|
Stream->Dev = SystemTable->ConIn;
|
|
break;
|
|
case STDOUT_FILENO:
|
|
Stream->Dev = SystemTable->ConOut;
|
|
break;
|
|
case STDERR_FILENO:
|
|
if(SystemTable->StdErr == NULL) {
|
|
Stream->Dev = SystemTable->ConOut;
|
|
}
|
|
else {
|
|
Stream->Dev = SystemTable->StdErr;
|
|
}
|
|
break;
|
|
default:
|
|
return RETURN_VOLUME_CORRUPTED; // This is a "should never happen" case.
|
|
}
|
|
|
|
Stream->Abstraction.fo_close = &da_ConClose;
|
|
Stream->Abstraction.fo_read = &da_ConRead;
|
|
Stream->Abstraction.fo_write = &da_ConWrite;
|
|
Stream->Abstraction.fo_stat = &da_ConStat;
|
|
Stream->Abstraction.fo_lseek = &da_ConSeek;
|
|
Stream->Abstraction.fo_fcntl = &fnullop_fcntl;
|
|
Stream->Abstraction.fo_ioctl = &da_ConIoctl;
|
|
Stream->Abstraction.fo_poll = &da_ConPoll;
|
|
Stream->Abstraction.fo_flush = &da_ConFlush;
|
|
Stream->Abstraction.fo_delete = &fbadop_delete;
|
|
Stream->Abstraction.fo_mkdir = &fbadop_mkdir;
|
|
Stream->Abstraction.fo_rmdir = &fbadop_rmdir;
|
|
Stream->Abstraction.fo_rename = &fbadop_rename;
|
|
|
|
Stream->NumRead = 0;
|
|
Stream->NumWritten = 0;
|
|
Stream->UnGetKey = CHAR_NULL;
|
|
|
|
if(Stream->Dev == NULL) {
|
|
continue; // No device for this stream.
|
|
}
|
|
ConNode[i] = __DevRegister(stdioNames[i], NULL, &da_ConOpen, Stream,
|
|
1, sizeof(ConInstance), stdioFlags[i]);
|
|
if(ConNode[i] == NULL) {
|
|
Status = EFIerrno; // Grab error code that DevRegister produced.
|
|
break;
|
|
}
|
|
Stream->Parent = ConNode[i];
|
|
}
|
|
/* Initialize Ioctl flags until Ioctl is really implemented. */
|
|
TtyCooked = TRUE;
|
|
TtyEcho = TRUE;
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
EFIAPI
|
|
__Cons_deconstruct(
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < NUM_SPECIAL; ++i) {
|
|
if(ConNode[i] != NULL) {
|
|
FreePool(ConNode[i]);
|
|
}
|
|
}
|
|
if(ConInstanceList != NULL) {
|
|
FreePool(ConInstanceList);
|
|
}
|
|
if(IIO != NULL) {
|
|
IIO->Delete(IIO);
|
|
IIO = NULL;
|
|
}
|
|
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
/* ######################################################################### */
|
|
#if 0 /* Not implemented (yet?) for Console */
|
|
|
|
static
|
|
int
|
|
EFIAPI
|
|
da_ConCntl(
|
|
struct __filedes *filp,
|
|
UINT32,
|
|
void *,
|
|
void *
|
|
)
|
|
{
|
|
}
|
|
#endif /* Not implemented for Console */
|