mirror of https://github.com/acidanthera/audk.git
418 lines
14 KiB
C
418 lines
14 KiB
C
/** @file
|
|
Definitions for the Interactive IO library.
|
|
|
|
The functions assume that isatty() is TRUE at the time they are called.
|
|
|
|
Copyright (c) 2016, Daryl McDaniel. All rights reserved.<BR>
|
|
Copyright (c) 2012, 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 which 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/MemoryAllocationLib.h>
|
|
|
|
#include <LibConfig.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <sys/syslimits.h>
|
|
#include <sys/termios.h>
|
|
#include <Device/IIO.h>
|
|
#include <MainData.h>
|
|
#include "IIOutilities.h"
|
|
#include "IIOechoCtrl.h"
|
|
|
|
// Instrumentation used for debugging
|
|
#define IIO_C_DEBUG 0 ///< Set to 1 to enable instrumentation, 0 to disable
|
|
|
|
#if IIO_C_DEBUG
|
|
static volatile size_t IIO_C_WRemainder = 0; ///< Characters in Out buffer after IIO_Write
|
|
static volatile size_t IIO_C_RRemainder = 0; ///< Characters in In buffer after IIO_Read
|
|
|
|
#define W_INSTRUMENT IIO_C_WRemainder =
|
|
#define R_INSTRUMENT IIO_C_RRemainder =
|
|
#else // ! IIO_C_DEBUG -- don't instrument code
|
|
#define W_INSTRUMENT (void)
|
|
#define R_INSTRUMENT (void)
|
|
#endif // IIO_C_DEBUG
|
|
|
|
/** Read from an Interactive IO device.
|
|
|
|
NOTE: If _S_IWTTY is set, the internal buffer contains WIDE characters.
|
|
They will need to be converted to MBCS when returned.
|
|
|
|
Input is line buffered if ICANON is set,
|
|
otherwise MIN determines how many characters to input.
|
|
Currently MIN is always zero, meaning 0 or 1 character is input in
|
|
noncanonical mode.
|
|
|
|
@param[in] filp Pointer to the descriptor of the device (file) to be read.
|
|
@param[in] BufferSize Maximum number of bytes to be returned to the caller.
|
|
@param[out] Buffer Pointer to the buffer where the input is to be stored.
|
|
|
|
@retval -1 An error occurred. No data is available.
|
|
@retval 0 No data was available. Try again later.
|
|
@retval >0 The number of bytes consumed by the returned data.
|
|
**/
|
|
static
|
|
ssize_t
|
|
EFIAPI
|
|
IIO_Read(
|
|
struct __filedes *filp,
|
|
size_t BufferSize,
|
|
VOID *Buffer
|
|
)
|
|
{
|
|
cIIO *This;
|
|
ssize_t NumRead;
|
|
tcflag_t Flags;
|
|
size_t XlateSz;
|
|
size_t Needed;
|
|
|
|
NumRead = -1;
|
|
This = filp->devdata;
|
|
if(This != NULL) {
|
|
Flags = This->Termio.c_lflag;
|
|
if(Flags & ICANON) {
|
|
NumRead = IIO_CanonRead(filp);
|
|
}
|
|
else {
|
|
NumRead = IIO_NonCanonRead(filp);
|
|
}
|
|
// At this point, the input has been accumulated in the input buffer.
|
|
if(filp->f_iflags & _S_IWTTY) {
|
|
// Data in InBuf is wide characters. Convert to MBCS
|
|
// First, convert into a linear buffer
|
|
NumRead = This->InBuf->Copy(This->InBuf, gMD->UString2, (INT32)UNICODE_STRING_MAX-1);
|
|
gMD->UString2[NumRead] = 0; // Ensure that the buffer is terminated
|
|
// Determine the needed space
|
|
XlateSz = EstimateWtoM((const wchar_t *)gMD->UString2, BufferSize, &Needed);
|
|
|
|
// Now translate this into MBCS in Buffer
|
|
NumRead = wcstombs((char *)Buffer, (const wchar_t *)gMD->UString2, XlateSz);
|
|
|
|
// Consume the translated characters
|
|
(void) This->InBuf->Flush(This->InBuf, Needed);
|
|
}
|
|
else {
|
|
// Data in InBuf is narrow characters. Use verbatim.
|
|
NumRead = This->InBuf->Read(This->InBuf, Buffer, (INT32)BufferSize);
|
|
}
|
|
#if IIO_C_DEBUG
|
|
IIO_C_RRemainder = This->InBuf->Count(This->InBuf, AsElements);
|
|
#endif // IIO_C_DEBUG
|
|
}
|
|
return NumRead;
|
|
}
|
|
|
|
/** Handle write to a Terminal (Interactive) device.
|
|
|
|
Processes characters from buffer buf and writes them to the Terminal device
|
|
specified by filp.
|
|
|
|
The parameter buf points to a MBCS string to be output. This is processed
|
|
and buffered one character at a time by IIO_WriteOne() which handles TAB
|
|
expansion, NEWLINE to CARRIAGE_RETURN + NEWLINE expansion, as well as
|
|
basic line editing functions. The number of characters actually written to
|
|
the output device will seldom equal the number of characters consumed from
|
|
buf.
|
|
|
|
In this implementation, all of the special characters processed by
|
|
IIO_WriteOne() are single-byte characters with values less than 128.
|
|
(7-bit ASCII or the single-byte UTF-8 characters)
|
|
|
|
Every byte that is not one of the recognized special characters is passed,
|
|
unchanged, to the Terminal device.
|
|
|
|
@param[in] filp Pointer to a file descriptor structure.
|
|
@param[in] buf Pointer to the MBCS string to be output.
|
|
@param[in] N Number of bytes in buf.
|
|
|
|
@retval >=0 Number of bytes consumed from buf and sent to the
|
|
Terminal device.
|
|
**/
|
|
static
|
|
ssize_t
|
|
EFIAPI
|
|
IIO_Write(
|
|
struct __filedes *filp,
|
|
const char *buf,
|
|
ssize_t N
|
|
)
|
|
{
|
|
cIIO *This;
|
|
cFIFO *OutBuf;
|
|
mbstate_t *OutState;
|
|
char *MbcsPtr;
|
|
ssize_t NumConsumed;
|
|
ssize_t NumProc;
|
|
size_t CharLen;
|
|
UINTN MaxColumn;
|
|
UINTN MaxRow;
|
|
wchar_t OutChar[2]; // Just in case we run into a 4-byte MBCS character
|
|
int OutMode;
|
|
|
|
NumConsumed = -1;
|
|
|
|
/* Determine what the current screen size is. Also validates the output device. */
|
|
OutMode = IIO_GetOutputSize(filp->MyFD, &MaxColumn, &MaxRow);
|
|
|
|
This = filp->devdata;
|
|
if((This != NULL) && (OutMode >= 0)) {
|
|
if(filp->MyFD == STDERR_FILENO) {
|
|
OutBuf = This->ErrBuf;
|
|
OutState = &This->ErrState;
|
|
}
|
|
else {
|
|
OutBuf = This->OutBuf;
|
|
OutState = &This->OutState;
|
|
}
|
|
|
|
/* Set the maximum screen dimensions. */
|
|
This->MaxColumn = MaxColumn;
|
|
This->MaxRow = MaxRow;
|
|
|
|
/* Record where the cursor is at the beginning of the Output operation. */
|
|
(void)IIO_GetCursorPosition(filp->MyFD, &This->InitialXY.Column, &This->InitialXY.Row);
|
|
This->CurrentXY.Column = This->InitialXY.Column;
|
|
This->CurrentXY.Row = This->InitialXY.Row;
|
|
|
|
NumConsumed = 0;
|
|
OutChar[0] = (wchar_t)buf[0];
|
|
while((OutChar[0] != 0) && (NumConsumed < N)) {
|
|
CharLen = mbrtowc(OutChar, (const char *)&buf[NumConsumed], MB_CUR_MAX, OutState);
|
|
if (CharLen < 0) { // Encoding Error
|
|
OutChar[0] = BLOCKELEMENT_LIGHT_SHADE;
|
|
CharLen = 1; // Consume a byte
|
|
(void)mbrtowc(NULL, NULL, 1, OutState); // Re-Initialize the conversion state
|
|
}
|
|
NumProc = IIO_WriteOne(filp, OutBuf, OutChar[0]);
|
|
if(NumProc >= 0) {
|
|
// Successfully processed and buffered one character
|
|
NumConsumed += CharLen; // Index of start of next character
|
|
}
|
|
else {
|
|
if (errno == ENOSPC) {
|
|
// Not enough room in OutBuf to hold a potentially expanded character
|
|
break;
|
|
}
|
|
return -1; // Something corrupted and filp->devdata is now NULL
|
|
}
|
|
}
|
|
// At this point, the characters to write are in OutBuf
|
|
// First, linearize the buffer
|
|
NumProc = OutBuf->Copy(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
|
|
gMD->UString[NumProc] = 0; // Ensure that the buffer is terminated
|
|
|
|
if(filp->f_iflags & _S_IWTTY) {
|
|
// Output device expects wide characters, Output what we have
|
|
NumProc = filp->f_ops->fo_write(filp, NULL, NumProc, gMD->UString);
|
|
|
|
// Consume the output characters
|
|
W_INSTRUMENT OutBuf->Flush(OutBuf, NumProc);
|
|
}
|
|
else {
|
|
// Output device expects narrow characters, convert to MBCS
|
|
MbcsPtr = (char *)gMD->UString2;
|
|
// Determine the needed space. NumProc is the number of bytes needed.
|
|
NumProc = (ssize_t)EstimateWtoM((const wchar_t *)gMD->UString, UNICODE_STRING_MAX * sizeof(wchar_t), &CharLen);
|
|
|
|
// Now translate this into MBCS in the buffer pointed to by MbcsPtr.
|
|
// The returned value, NumProc, is the resulting number of bytes.
|
|
NumProc = wcstombs(MbcsPtr, (const wchar_t *)gMD->UString, NumProc);
|
|
MbcsPtr[NumProc] = 0; // Ensure the buffer is terminated
|
|
|
|
// Send the MBCS buffer to Output
|
|
NumProc = filp->f_ops->fo_write(filp, NULL, NumProc, MbcsPtr);
|
|
// Mark the Mbcs buffer after the last byte actually written
|
|
MbcsPtr[NumProc] = 0;
|
|
// Count the CHARACTERS actually sent
|
|
CharLen = CountMbcsChars(MbcsPtr);
|
|
|
|
// Consume the number of output characters actually sent
|
|
W_INSTRUMENT OutBuf->Flush(OutBuf, CharLen);
|
|
}
|
|
}
|
|
else {
|
|
if(This == NULL) {
|
|
errno = EINVAL;
|
|
}
|
|
// Otherwise, errno is already set.
|
|
}
|
|
return NumConsumed;
|
|
}
|
|
|
|
/** Echo a character to an output device.
|
|
Performs translation and edit processing depending upon termios flags.
|
|
|
|
@param[in] filp A pointer to a file descriptor structure.
|
|
@param[in] EChar The character to echo.
|
|
@param[in] EchoIsOK TRUE if the caller has determined that characters
|
|
should be echoed. Otherwise, just buffer.
|
|
|
|
@return Returns the number of characters actually output.
|
|
**/
|
|
static
|
|
ssize_t
|
|
EFIAPI
|
|
IIO_Echo(
|
|
struct __filedes *filp,
|
|
wchar_t EChar,
|
|
BOOLEAN EchoIsOK
|
|
)
|
|
{
|
|
cIIO *This;
|
|
ssize_t NumWritten;
|
|
cFIFO *OutBuf;
|
|
char *MbcsPtr;
|
|
ssize_t NumProc;
|
|
tcflag_t LFlags;
|
|
|
|
NumWritten = -1;
|
|
This = filp->devdata;
|
|
if(This != NULL) {
|
|
OutBuf = This->OutBuf;
|
|
LFlags = This->Termio.c_lflag & (ECHOK | ECHOE);
|
|
|
|
if((EChar >= TtyFunKeyMin) && (EChar < TtyFunKeyMax)) {
|
|
// A special function key was pressed, buffer it, don't echo, and activate.
|
|
// Process and buffer the character. May produce multiple characters.
|
|
NumProc = IIO_EchoOne(filp, EChar, FALSE); // Don't echo this character
|
|
EChar = CHAR_LINEFEED; // Every line must end with '\n' (legacy)
|
|
}
|
|
// Process and buffer the character. May produce multiple characters.
|
|
NumProc = IIO_EchoOne(filp, EChar, EchoIsOK);
|
|
|
|
// At this point, the character(s) to write are in OutBuf
|
|
// First, linearize the buffer
|
|
NumWritten = OutBuf->Copy(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
|
|
gMD->UString[NumWritten] = 0; // Ensure that the buffer is terminated
|
|
|
|
if((EChar == IIO_ECHO_KILL) && (LFlags & ECHOE) && EchoIsOK) {
|
|
// Position the cursor to the start of input.
|
|
(void)IIO_SetCursorPosition(filp, &This->InitialXY);
|
|
}
|
|
// Output the buffer
|
|
if(filp->f_iflags & _S_IWTTY) {
|
|
// Output device expects wide characters, Output what we have
|
|
NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, gMD->UString);
|
|
}
|
|
else {
|
|
// Output device expects narrow characters, convert to MBCS
|
|
MbcsPtr = (char *)gMD->UString2;
|
|
// Determine the needed space
|
|
NumProc = (ssize_t)EstimateWtoM((const wchar_t *)gMD->UString, UNICODE_STRING_MAX * sizeof(wchar_t), NULL);
|
|
|
|
// Now translate this into MBCS in Buffer
|
|
NumWritten = wcstombs(MbcsPtr, (const wchar_t *)gMD->UString, NumProc);
|
|
MbcsPtr[NumWritten] = 0; // Ensure the buffer is terminated
|
|
|
|
// Send the MBCS buffer to Output
|
|
NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, MbcsPtr);
|
|
}
|
|
// Consume the echoed characters
|
|
(void)OutBuf->Flush(OutBuf, NumWritten);
|
|
|
|
if(EChar == IIO_ECHO_KILL) {
|
|
if(LFlags == ECHOK) {
|
|
NumWritten = IIO_WriteOne(filp, OutBuf, CHAR_LINEFEED);
|
|
}
|
|
else if((LFlags & ECHOE) && EchoIsOK) {
|
|
// Position the cursor to the start of input.
|
|
(void)IIO_SetCursorPosition(filp, &This->InitialXY);
|
|
}
|
|
NumWritten = 0;
|
|
}
|
|
}
|
|
else {
|
|
errno = EINVAL;
|
|
}
|
|
|
|
return NumWritten;
|
|
}
|
|
|
|
static
|
|
void
|
|
FifoDelete(cFIFO *Member)
|
|
{
|
|
if(Member != NULL) {
|
|
Member->Delete(Member);
|
|
}
|
|
}
|
|
|
|
/** Destructor for an IIO instance.
|
|
|
|
Releases all resources used by a particular IIO instance.
|
|
**/
|
|
static
|
|
void
|
|
EFIAPI
|
|
IIO_Delete(
|
|
cIIO *Self
|
|
)
|
|
{
|
|
if(Self != NULL) {
|
|
FifoDelete(Self->ErrBuf);
|
|
FifoDelete(Self->OutBuf);
|
|
FifoDelete(Self->InBuf);
|
|
if(Self->AttrBuf != NULL) {
|
|
FreePool(Self->AttrBuf);
|
|
}
|
|
FreePool(Self);
|
|
}
|
|
}
|
|
|
|
/** Constructor for new IIO instances.
|
|
|
|
@return Returns NULL or a pointer to a new IIO instance.
|
|
**/
|
|
cIIO *
|
|
EFIAPI
|
|
New_cIIO(void)
|
|
{
|
|
cIIO *IIO;
|
|
cc_t *TempBuf;
|
|
int i;
|
|
|
|
IIO = (cIIO *)AllocateZeroPool(sizeof(cIIO));
|
|
if(IIO != NULL) {
|
|
IIO->InBuf = New_cFIFO(MAX_INPUT, sizeof(wchar_t));
|
|
IIO->OutBuf = New_cFIFO(MAX_OUTPUT, sizeof(wchar_t));
|
|
IIO->ErrBuf = New_cFIFO(MAX_OUTPUT, sizeof(wchar_t));
|
|
IIO->AttrBuf = (UINT8 *)AllocateZeroPool(MAX_OUTPUT);
|
|
|
|
if((IIO->InBuf == NULL) || (IIO->OutBuf == NULL) ||
|
|
(IIO->ErrBuf == NULL) || (IIO->AttrBuf == NULL))
|
|
{
|
|
IIO_Delete(IIO);
|
|
IIO = NULL;
|
|
}
|
|
else {
|
|
IIO->Delete = IIO_Delete;
|
|
IIO->Read = IIO_Read;
|
|
IIO->Write = IIO_Write;
|
|
IIO->Echo = IIO_Echo;
|
|
}
|
|
// Initialize Termio member
|
|
TempBuf = &IIO->Termio.c_cc[0];
|
|
TempBuf[0] = 8; // Default length for TABs
|
|
for(i=1; i < NCCS; ++i) {
|
|
TempBuf[i] = _POSIX_VDISABLE;
|
|
}
|
|
TempBuf[VMIN] = 0;
|
|
TempBuf[VTIME] = 0;
|
|
IIO->Termio.c_ispeed = B115200;
|
|
IIO->Termio.c_ospeed = B115200;
|
|
IIO->Termio.c_iflag = ICRNL;
|
|
IIO->Termio.c_oflag = OPOST | ONLCR | ONOCR | ONLRET;
|
|
IIO->Termio.c_cflag = 0;
|
|
IIO->Termio.c_lflag = ECHO | ECHONL;
|
|
}
|
|
return IIO;
|
|
}
|