Home | History | Annotate | Download | only in Console
      1 /** @file
      2   Abstract device driver for the UEFI Console.
      3 
      4   Manipulates abstractions for stdin, stdout, stderr.
      5 
      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.
      9 
     10   The devices status as a wide device is indicatd by _S_IWTTY being set in
     11   f_iflags.
     12 
     13   Copyright (c) 2016, Daryl McDaniel. All rights reserved.<BR>
     14   Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
     15   This program and the accompanying materials are licensed and made available under
     16   the terms and conditions of the BSD License that accompanies this distribution.
     17   The full text of the license may be found at
     18   http://opensource.org/licenses/bsd-license.php.
     19 
     20   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     21   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     22 
     23 **/
     24 #include  <Uefi.h>
     25 #include  <Library/BaseLib.h>
     26 #include  <Library/MemoryAllocationLib.h>
     27 #include  <Library/UefiBootServicesTableLib.h>
     28 #include  <Library/DebugLib.h>
     29 #include  <Protocol/SimpleTextIn.h>
     30 #include  <Protocol/SimpleTextOut.h>
     31 
     32 #include  <LibConfig.h>
     33 
     34 #include  <errno.h>
     35 #include  <wctype.h>
     36 #include  <wchar.h>
     37 #include  <stdarg.h>
     38 #include  <sys/fcntl.h>
     39 #include  <unistd.h>
     40 #include  <sys/termios.h>
     41 #include  <Efi/SysEfi.h>
     42 #include  <kfile.h>
     43 #include  <Device/Device.h>
     44 #include  <Device/IIO.h>
     45 #include  <MainData.h>
     46 
     47 static const CHAR16* const
     48 stdioNames[NUM_SPECIAL]   = {
     49   L"stdin:", L"stdout:", L"stderr:"
     50 };
     51 
     52 static const int stdioFlags[NUM_SPECIAL] = {
     53   O_RDONLY,             // stdin
     54   O_WRONLY,             // stdout
     55   O_WRONLY              // stderr
     56 };
     57 
     58 static DeviceNode    *ConNode[NUM_SPECIAL];
     59 static ConInstance   *ConInstanceList;
     60 
     61 static cIIO          *IIO;
     62 
     63 /* Flags settable by Ioctl */
     64 static BOOLEAN        TtyCooked;
     65 static BOOLEAN        TtyEcho;
     66 
     67 /** Convert string from MBCS to WCS and translate \n to \r\n.
     68 
     69     It is the caller's responsibility to ensure that dest is
     70     large enough to hold the converted results.  It is guaranteed
     71     that there will be fewer than n characters placed in dest.
     72 
     73     @param[out]     dest    WCS buffer to receive the converted string.
     74     @param[in]      buf     MBCS string to convert to WCS.
     75     @param[in]      n       Number of BYTES contained in buf.
     76     @param[in,out]  Cs      Pointer to the character state object for this stream
     77 
     78     @return   The number of BYTES consumed from buf.
     79 **/
     80 ssize_t
     81 WideTtyCvt( CHAR16 *dest, const char *buf, ssize_t n, mbstate_t *Cs)
     82 {
     83   ssize_t i     = 0;
     84   int     numB  = 0;
     85   wchar_t wc[2];
     86 
     87   while(n > 0) {
     88     numB = (int)mbrtowc(wc, buf, MIN(MB_LEN_MAX,n), Cs);
     89     if( numB == 0) {
     90       break;
     91     };
     92     if(numB < 0) {    // If an unconvertable character, replace it.
     93       wc[0] = BLOCKELEMENT_LIGHT_SHADE;
     94       numB = 1;
     95     }
     96     if(wc[0] == L'\n') {
     97       *dest++ = L'\r';
     98       ++i;
     99     }
    100     *dest++ = (CHAR16)wc[0];
    101     i += numB;
    102     n -= numB;
    103     buf += numB;
    104   }
    105   *dest = 0;
    106   return i;
    107 }
    108 
    109 /** Position the console cursor to the coordinates specified by Position.
    110 
    111     @param[in]  filp      Pointer to the file descriptor structure for this file.
    112     @param[in]  Position  A value containing the target X and Y coordinates.
    113     @param[in]  whence    Ignored by the Console device.
    114 
    115     @retval   Position    Success.  Returns a copy of the Position argument.
    116     @retval   -1          filp is not associated with a valid console stream.
    117     @retval   -1          This console stream is attached to stdin.
    118     @retval   -1          The SetCursorPosition operation failed.
    119 **/
    120 static
    121 off_t
    122 EFIAPI
    123 da_ConSeek(
    124   struct __filedes   *filp,
    125   off_t               Position,
    126   int                 whence      ///< Ignored by Console
    127 )
    128 {
    129   ConInstance                       *Stream;
    130   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL   *Proto;
    131   XY_OFFSET                          CursorPos;
    132 
    133   Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
    134   // Quick check to see if Stream looks reasonable
    135   if(Stream->Cookie != CON_COOKIE) {    // Cookie == 'IoAb'
    136     EFIerrno = RETURN_INVALID_PARAMETER;
    137     return -1;    // Looks like a bad This pointer
    138   }
    139   if(Stream->InstanceNum == STDIN_FILENO) {
    140     // Seek is not valid for stdin
    141     EFIerrno = RETURN_UNSUPPORTED;
    142     return -1;
    143   }
    144   // Everything is OK to do the final verification and "seek".
    145   Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
    146   CursorPos.Offset = Position;
    147 
    148   EFIerrno = Proto->SetCursorPosition(Proto,
    149                                       (INTN)CursorPos.XYpos.Column,
    150                                       (INTN)CursorPos.XYpos.Row);
    151 
    152   if(RETURN_ERROR(EFIerrno)) {
    153     return -1;
    154   }
    155   else {
    156     return Position;
    157   }
    158 }
    159 
    160 /* Write a NULL terminated WCS to the EFI console.
    161 
    162   NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters received
    163         by da_ConWrite are WIDE characters.  It is the responsibility of the
    164         higher-level function(s) to perform any necessary conversions.
    165 
    166   @param[in,out]  BufferSize  Number of characters in Buffer.
    167   @param[in]      Buffer      The WCS string to be displayed
    168 
    169   @return   The number of Characters written.
    170 */
    171 static
    172 ssize_t
    173 EFIAPI
    174 da_ConWrite(
    175   IN  struct __filedes     *filp,
    176   IN  off_t                *Position,
    177   IN  size_t                BufferSize,
    178   IN  const void           *Buffer
    179   )
    180 {
    181   EFI_STATUS                          Status;
    182   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *Proto;
    183   ConInstance                        *Stream;
    184   ssize_t                             NumChar;
    185   XY_OFFSET                          CursorPos;
    186 
    187   NumChar = -1;
    188   Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
    189   // Quick check to see if Stream looks reasonable
    190   if(Stream->Cookie != CON_COOKIE) {    // Cookie == 'IoAb'
    191     EFIerrno = RETURN_INVALID_PARAMETER;
    192     return -1;    // Looks like a bad This pointer
    193   }
    194   if(Stream->InstanceNum == STDIN_FILENO) {
    195     // Write is not valid for stdin
    196     EFIerrno = RETURN_UNSUPPORTED;
    197     return -1;
    198   }
    199   // Everything is OK to do the write.
    200   Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
    201 
    202   Status = EFI_SUCCESS;
    203   if(Position != NULL) {
    204     CursorPos.Offset = *Position;
    205 
    206     Status = Proto->SetCursorPosition(Proto,
    207                                       (INTN)CursorPos.XYpos.Column,
    208                                       (INTN)CursorPos.XYpos.Row);
    209 
    210   }
    211   if(!RETURN_ERROR(Status)) {
    212   // Send the Unicode buffer to the console
    213     Status = Proto->OutputString( Proto, (CHAR16 *)Buffer);
    214   }
    215 
    216   // Depending on status, update BufferSize and return
    217   if(!RETURN_ERROR(Status)) {
    218     NumChar = BufferSize;
    219     Stream->NumWritten += NumChar;
    220   }
    221   EFIerrno = Status;      // Make error reason available to caller
    222   return NumChar;
    223 }
    224 
    225 /** Read a wide character from the console input device.
    226 
    227     Returns NUL or a translated input character.
    228 
    229     @param[in]      filp          Pointer to file descriptor for this file.
    230     @param[out]     Buffer        Buffer in which to place the read character.
    231 
    232     @retval    EFI_DEVICE_ERROR   A hardware error has occurred.
    233     @retval    EFI_NOT_READY      No data is available.  Try again later.
    234     @retval    EFI_SUCCESS        One wide character has been placed in Character
    235                                     - 0x0000  NUL, ignore this
    236                                     - Otherwise, should be a good wide character in Character
    237 **/
    238 static
    239 EFI_STATUS
    240 da_ConRawRead (
    241   IN OUT  struct __filedes   *filp,
    242      OUT  wchar_t            *Character
    243 )
    244 {
    245   EFI_SIMPLE_TEXT_INPUT_PROTOCOL   *Proto;
    246   ConInstance                      *Stream;
    247   cIIO                             *Self;
    248   EFI_STATUS                        Status;
    249   EFI_INPUT_KEY                     Key = {0,0};
    250   wchar_t                           RetChar;
    251 
    252   Self    = (cIIO *)filp->devdata;
    253   Stream  = BASE_CR(filp->f_ops, ConInstance, Abstraction);
    254   Proto   = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
    255 
    256   if(Stream->UnGetKey == CHAR_NULL) {
    257     Status = Proto->ReadKeyStroke(Proto, &Key);
    258   }
    259   else {
    260     Status  = EFI_SUCCESS;
    261     // Use the data in the Un-get buffer
    262     // Guaranteed that ScanCode and UnicodeChar are not both NUL
    263     Key.ScanCode        = SCAN_NULL;
    264     Key.UnicodeChar     = Stream->UnGetKey;
    265     Stream->UnGetKey    = CHAR_NULL;
    266   }
    267   if(Status == EFI_SUCCESS) {
    268     // Translate the Escape Scan Code to an ESC character
    269     if (Key.ScanCode != 0) {
    270       if (Key.ScanCode == SCAN_ESC) {
    271         RetChar = CHAR_ESC;
    272       }
    273       else if((Self->Termio.c_iflag & IGNSPEC) != 0) {
    274         // If we are ignoring special characters, return a NUL
    275         RetChar = 0;
    276       }
    277       else {
    278         // Must be a control, function, or other non-printable key.
    279         // Map it into the Platform portion of the Unicode private use area
    280         RetChar = TtyFunKeyMax - Key.ScanCode;
    281       }
    282     }
    283     else {
    284       RetChar = Key.UnicodeChar;
    285     }
    286     *Character = RetChar;
    287   }
    288   else {
    289     *Character = 0;
    290   }
    291   return Status;
    292 }
    293 
    294 /** Read a wide character from the console input device.
    295 
    296   NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters returned
    297         by da_ConRead are WIDE characters.  It is the responsibility of the
    298         higher-level function(s) to perform any necessary conversions.
    299 
    300     A NUL character, 0x0000, is never returned.  In the event that such a character
    301     is encountered, the read is either retried or -1 is returned with errno set
    302     to EAGAIN.
    303 
    304     @param[in]      filp          Pointer to file descriptor for this file.
    305     @param[in]      offset        Ignored.
    306     @param[in]      BufferSize    Buffer size, in bytes.
    307     @param[out]     Buffer        Buffer in which to place the read characters.
    308 
    309     @retval    -1   An error has occurred.  Reason in errno and EFIerrno.
    310     @retval    -1   No data is available.  errno is set to EAGAIN
    311     @retval     1   One wide character has been placed in Buffer
    312 **/
    313 static
    314 ssize_t
    315 EFIAPI
    316 da_ConRead(
    317   IN OUT  struct __filedes   *filp,
    318   IN OUT  off_t              *offset,         // Console ignores this
    319   IN      size_t              BufferSize,
    320      OUT  VOID               *Buffer
    321 )
    322 {
    323   EFI_SIMPLE_TEXT_INPUT_PROTOCOL   *Proto;
    324   ConInstance                      *Stream;
    325   //cIIO                              *Self;
    326   EFI_STATUS                        Status;
    327   UINTN                             Edex;
    328   ssize_t                           NumRead;
    329   BOOLEAN                           BlockingMode;
    330   wchar_t                           RetChar;
    331 
    332   NumRead = -1;
    333   if(BufferSize < sizeof(wchar_t)) {
    334     errno = EINVAL;     // Buffer is too small to hold one character
    335   }
    336   else {
    337     Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
    338     Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
    339     BlockingMode = (BOOLEAN)((filp->Oflags & O_NONBLOCK) == 0);
    340 
    341     do {
    342       Status = EFI_SUCCESS;
    343       if(BlockingMode) {
    344         // Read a byte in Blocking mode
    345         Status = gBS->WaitForEvent( 1, &Proto->WaitForKey, &Edex);
    346       }
    347 
    348       /*  WaitForEvent should not be able to fail since
    349             NumberOfEvents is set to constant 1 so is never 0
    350             Event is set by the Simple Text Input protocol so should never be EVT_NOTIFY_SIGNAL
    351             Current TPL should be TPL_APPLICATION.
    352           ASSERT so that we catch any problems during development.
    353       */
    354       ASSERT(Status == EFI_SUCCESS);
    355 
    356       Status = da_ConRawRead (filp, &RetChar);
    357     } while ( BlockingMode &&
    358              (RetChar == 0) &&
    359              (Status != EFI_DEVICE_ERROR));
    360 
    361     EFIerrno = Status;
    362     if(Status == EFI_SUCCESS) {
    363       // Got a keystroke.
    364       NumRead = 1;   // Indicate that Key holds the data
    365     }
    366     else if(Status == EFI_NOT_READY) {
    367       // Keystroke data is not available
    368       errno = EAGAIN;
    369     }
    370     else {
    371       // Hardware error
    372       errno = EIO;
    373     }
    374     if (RetChar == 0) {
    375       NumRead = -1;
    376       errno = EAGAIN;
    377     }
    378     else {
    379       *((wchar_t *)Buffer) = RetChar;
    380     }
    381   }
    382   return NumRead;
    383 }
    384 
    385 /** Console-specific helper function for the fstat() function.
    386 
    387     st_size       Set to number of characters read for stdin and number written for stdout and stderr.
    388     st_physsize   1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
    389     st_curpos     0 for stdin, current X & Y coordinates for stdout and stderr
    390     st_blksize    Set to 1 since this is a character device
    391 
    392     All other members of the stat structure are left unchanged.
    393 
    394     @param[in]      filp          Pointer to file descriptor for this file.
    395     @param[out]     Buffer        Pointer to a stat structure to receive the information.
    396     @param[in,out]  Something     Ignored.
    397 
    398     @retval   0   Successful completion.
    399     @retval   -1  Either filp is not associated with a console stream, or
    400                   Buffer is NULL.  errno is set to EINVAL.
    401 **/
    402 static
    403 int
    404 EFIAPI
    405 da_ConStat(
    406   struct __filedes   *filp,
    407   struct stat        *Buffer,
    408   void               *Something
    409   )
    410 {
    411   ConInstance                        *Stream;
    412   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *Proto;
    413   XY_OFFSET                           CursorPos;
    414   INT32                               OutMode;
    415   UINTN                               ModeCol;
    416   UINTN                               ModeRow;
    417 
    418 // ConGetInfo
    419   Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
    420   // Quick check to see if Stream looks reasonable
    421   if ((Stream->Cookie != CON_COOKIE) ||    // Cookie == 'IoAb'
    422       (Buffer == NULL))
    423   {
    424     errno     = EINVAL;
    425     EFIerrno = RETURN_INVALID_PARAMETER;
    426     return -1;
    427   }
    428   // All of our parameters are correct, so fill in the information.
    429   Buffer->st_blksize  = 0;   // Character device, not a block device
    430   Buffer->st_mode     = filp->f_iflags;
    431 
    432 // ConGetPosition
    433   if(Stream->InstanceNum == STDIN_FILENO) {
    434     // This is stdin
    435     Buffer->st_curpos    = 0;
    436     Buffer->st_size      = (off_t)Stream->NumRead;
    437     Buffer->st_physsize  = 1;
    438   }
    439   else {
    440     Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
    441     CursorPos.XYpos.Column  = (UINT32)Proto->Mode->CursorColumn;
    442     CursorPos.XYpos.Row     = (UINT32)Proto->Mode->CursorRow;
    443     Buffer->st_curpos       = (off_t)CursorPos.Offset;
    444     Buffer->st_size         = (off_t)Stream->NumWritten;
    445 
    446     OutMode  = Proto->Mode->Mode;
    447     EFIerrno = Proto->QueryMode(Proto, (UINTN)OutMode, &ModeCol, &ModeRow);
    448     if(RETURN_ERROR(EFIerrno)) {
    449       Buffer->st_physsize = 0;
    450     }
    451     else {
    452       CursorPos.XYpos.Column  = (UINT32)ModeCol;
    453       CursorPos.XYpos.Row     = (UINT32)ModeRow;
    454       Buffer->st_physsize     = (off_t)CursorPos.Offset;
    455     }
    456   }
    457   return 0;
    458 }
    459 
    460 /** Console-specific helper for the ioctl system call.
    461 
    462     The console device does not directly participate in ioctl operations.
    463     This function completes the device abstraction and returns an error value
    464     to indicate that the function is not supported for this device.
    465 
    466     @retval   -1    Function is not supported for this device.
    467 **/
    468 static
    469 int
    470 EFIAPI
    471 da_ConIoctl(
    472   struct __filedes   *filp,
    473   ULONGN              cmd,
    474   va_list             argp
    475   )
    476 {
    477   errno   = ENODEV;
    478   return  -1;
    479 }
    480 
    481 /** Open an abstract Console Device.
    482 
    483     @param[in]    DevNode       Pointer to the Device control structure for this stream.
    484     @param[in]    filp          Pointer to the new file control structure for this stream.
    485     @param[in]    DevInstance   Not used for the console device.
    486     @param[in]    Path          Not used for the console device.
    487     @param[in]    MPath         Not used for the console device.
    488 
    489     @retval   0   This console stream has been successfully opened.
    490     @retval   -1  The DevNode or filp pointer is NULL.
    491     @retval   -1  DevNode does not point to a valid console stream device.
    492 **/
    493 int
    494 EFIAPI
    495 da_ConOpen(
    496   DeviceNode         *DevNode,
    497   struct __filedes   *filp,
    498   int                 DevInstance,    // Not used for console devices
    499   wchar_t            *Path,           // Not used for console devices
    500   wchar_t            *MPath           // Not used for console devices
    501   )
    502 {
    503   ConInstance    *Stream;
    504   UINT32          Instance;
    505   int             RetVal = -1;
    506 
    507   if((filp    != NULL)    &&
    508       (DevNode != NULL))
    509   {
    510   Stream = (ConInstance *)DevNode->InstanceList;
    511   // Quick check to see if Stream looks reasonable
    512     if(Stream->Cookie == CON_COOKIE)
    513     {
    514       Instance = Stream->InstanceNum;
    515       if(Instance < NUM_SPECIAL) {
    516         gMD->StdIo[Instance] = Stream;
    517         filp->f_iflags |= (_S_IFCHR | _S_ITTY | _S_IWTTY | _S_ICONSOLE);
    518         filp->f_offset = 0;
    519         filp->f_ops = &Stream->Abstraction;
    520         filp->devdata = (void *)IIO;
    521         RetVal = 0;
    522       }
    523     }
    524   }
    525   if (RetVal < 0) {
    526     EFIerrno = RETURN_INVALID_PARAMETER;
    527     errno = EINVAL;
    528   }
    529   return RetVal;
    530 
    531 }
    532 
    533 /** Flush a console device's IIO buffers.
    534 
    535     Flush the IIO Input or Output buffers associated with the specified file.
    536 
    537     If the console is open for output, write any unwritten data in the associated
    538     output buffer (stdout or stderr) to the console.
    539 
    540     If the console is open for input, discard any remaining data
    541     in the input buffer.
    542 
    543     @param[in]    filp    Pointer to the target file's descriptor structure.
    544 
    545     @retval     0     Always succeeds
    546 **/
    547 static
    548 int
    549 EFIAPI
    550 da_ConFlush(
    551   struct __filedes *filp
    552 )
    553 {
    554   cFIFO      *OutBuf;
    555   ssize_t     NumProc;
    556   int         Flags;
    557 
    558 
    559     if(filp->MyFD == STDERR_FILENO) {
    560       OutBuf = IIO->ErrBuf;
    561     }
    562     else {
    563       OutBuf = IIO->OutBuf;
    564     }
    565 
    566     Flags = filp->Oflags & O_ACCMODE;   // Get the device's open mode
    567     if (Flags != O_WRONLY)  {   // (Flags == O_RDONLY) || (Flags == O_RDWR)
    568       // Readable so discard the contents of the input buffer
    569       IIO->InBuf->Flush(IIO->InBuf, UNICODE_STRING_MAX);
    570     }
    571     if (Flags != O_RDONLY)  {   // (Flags == O_WRONLY) || (Flags == O_RDWR)
    572       // Writable so flush the output buffer
    573       // At this point, the characters to write are in OutBuf
    574       // First, linearize and consume the buffer
    575       NumProc = OutBuf->Read(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
    576       if (NumProc > 0) {  // Optimization -- Nothing to do if no characters
    577         gMD->UString[NumProc] = 0;   // Ensure that the buffer is terminated
    578 
    579         /*  OutBuf always contains wide characters.
    580             The UEFI Console (this device) always expects wide characters.
    581             There is no need to handle devices that expect narrow characters
    582             like the device-independent functions do.
    583         */
    584         // Do the actual write of the data to the console
    585         (void) da_ConWrite(filp, NULL, NumProc, gMD->UString);
    586         // Paranoia -- Make absolutely sure that OutBuf is empty in case fo_write
    587         // wasn't able to consume everything.
    588         OutBuf->Flush(OutBuf, UNICODE_STRING_MAX);
    589       }
    590     }
    591   return 0;
    592 }
    593 
    594 /** Close an open file.
    595 
    596     @param[in]  filp    Pointer to the file descriptor structure for this file.
    597 
    598     @retval   0     The file has been successfully closed.
    599     @retval   -1    filp does not point to a valid console descriptor.
    600 **/
    601 static
    602 int
    603 EFIAPI
    604 da_ConClose(
    605   IN      struct __filedes   *filp
    606 )
    607 {
    608   ConInstance    *Stream;
    609 
    610   Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
    611   // Quick check to see if Stream looks reasonable
    612   if(Stream->Cookie != CON_COOKIE) {    // Cookie == 'IoAb'
    613     errno     = EINVAL;
    614     EFIerrno = RETURN_INVALID_PARAMETER;
    615     return -1;    // Looks like a bad File Descriptor pointer
    616   }
    617   // Stream and filp look OK, so continue.
    618   // Flush the I/O buffers
    619   (void) da_ConFlush(filp);
    620 
    621   // Break the connection to IIO
    622   filp->devdata = NULL;
    623 
    624   gMD->StdIo[Stream->InstanceNum] = NULL;   // Mark the stream as closed
    625   return 0;
    626 }
    627 
    628 #include  <sys/poll.h>
    629 /*  Returns a bit mask describing which operations could be completed immediately.
    630 
    631     Testable Events for this device are:
    632     (POLLIN | POLLRDNORM)   A Unicode character is available to read
    633     (POLLIN)                A ScanCode is ready.
    634     (POLLOUT)               The device is ready for output - always set on stdout and stderr.
    635 
    636     Non-testable Events which are only valid in return values are:
    637       POLLERR                 The specified device is not one of stdin, stdout, or stderr.
    638       POLLHUP                 The specified stream has been disconnected
    639       POLLNVAL                da_ConPoll was called with an invalid parameter.
    640 
    641   NOTE: The "Events" handled by this function are not UEFI events.
    642 
    643     @param[in]  filp      Pointer to the file control structure for this stream.
    644     @param[in]  events    A bit mask identifying the events to be examined
    645                           for this device.
    646 
    647     @return   Returns a bit mask comprised of both testable and non-testable
    648               event codes indicating both the state of the operation and the
    649               status of the device.
    650 */
    651 static
    652 short
    653 EFIAPI
    654 da_ConPoll(
    655   struct __filedes   *filp,
    656   short              events
    657   )
    658 {
    659   ConInstance                      *Stream;
    660   EFI_STATUS                        Status = RETURN_SUCCESS;
    661   short                             RdyMask = 0;
    662 
    663   Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
    664   // Quick check to see if Stream looks reasonable
    665   if(Stream->Cookie != CON_COOKIE) {    // Cookie == 'IoAb'
    666     errno     = EINVAL;
    667     EFIerrno = RETURN_INVALID_PARAMETER;
    668     return POLLNVAL;    // Looks like a bad filp pointer
    669   }
    670   if(Stream->InstanceNum == 0) {
    671     // STDIN: Only input is supported for this device
    672     Status = da_ConRawRead (filp, &Stream->UnGetKey);
    673     if(Status == RETURN_SUCCESS) {
    674       RdyMask = POLLIN;
    675       if ((Stream->UnGetKey <  TtyFunKeyMin)   ||
    676           (Stream->UnGetKey >= TtyFunKeyMax))
    677       {
    678         RdyMask |= POLLRDNORM;
    679       }
    680     }
    681     else {
    682       Stream->UnGetKey  = CHAR_NULL;
    683     }
    684   }
    685   else if(Stream->InstanceNum < NUM_SPECIAL) {  // Not 0, is it 1 or 2?
    686     // (STDOUT || STDERR): Only output is supported for this device
    687     RdyMask = POLLOUT;
    688   }
    689   else {
    690     RdyMask = POLLERR;    // Not one of the standard streams
    691   }
    692   EFIerrno = Status;
    693 
    694   return (RdyMask & (events | POLL_RETONLY));
    695 }
    696 
    697 /** Construct the Console stream devices: stdin, stdout, stderr.
    698 
    699     Allocate the instance structure and populate it with the information for
    700     each stream device.
    701 **/
    702 RETURN_STATUS
    703 EFIAPI
    704 __Cons_construct(
    705   IN EFI_HANDLE        ImageHandle,
    706   IN EFI_SYSTEM_TABLE  *SystemTable
    707 )
    708 {
    709   ConInstance    *Stream;
    710   RETURN_STATUS   Status;
    711   int             i;
    712 
    713   Status = RETURN_OUT_OF_RESOURCES;
    714   ConInstanceList = (ConInstance *)AllocateZeroPool(NUM_SPECIAL * sizeof(ConInstance));
    715   if(ConInstanceList != NULL) {
    716     IIO = New_cIIO();
    717     if(IIO == NULL) {
    718       FreePool(ConInstanceList);
    719     }
    720     else {
    721       Status = RETURN_SUCCESS;
    722       for( i = 0; i < NUM_SPECIAL; ++i) {
    723         // Get pointer to instance.
    724         Stream = &ConInstanceList[i];
    725 
    726         Stream->Cookie      = CON_COOKIE;
    727         Stream->InstanceNum = i;
    728         Stream->CharState.A = 0;    // Start in the initial state
    729 
    730         switch(i) {
    731           case STDIN_FILENO:
    732             Stream->Dev = SystemTable->ConIn;
    733             break;
    734           case STDOUT_FILENO:
    735             Stream->Dev = SystemTable->ConOut;
    736             break;
    737           case STDERR_FILENO:
    738             if(SystemTable->StdErr == NULL) {
    739               Stream->Dev = SystemTable->ConOut;
    740             }
    741             else {
    742               Stream->Dev = SystemTable->StdErr;
    743             }
    744             break;
    745           default:
    746             return RETURN_VOLUME_CORRUPTED;     // This is a "should never happen" case.
    747         }
    748 
    749         Stream->Abstraction.fo_close    = &da_ConClose;
    750         Stream->Abstraction.fo_read     = &da_ConRead;
    751         Stream->Abstraction.fo_write    = &da_ConWrite;
    752         Stream->Abstraction.fo_stat     = &da_ConStat;
    753         Stream->Abstraction.fo_lseek    = &da_ConSeek;
    754         Stream->Abstraction.fo_fcntl    = &fnullop_fcntl;
    755         Stream->Abstraction.fo_ioctl    = &da_ConIoctl;
    756         Stream->Abstraction.fo_poll     = &da_ConPoll;
    757         Stream->Abstraction.fo_flush    = &da_ConFlush;
    758         Stream->Abstraction.fo_delete   = &fbadop_delete;
    759         Stream->Abstraction.fo_mkdir    = &fbadop_mkdir;
    760         Stream->Abstraction.fo_rmdir    = &fbadop_rmdir;
    761         Stream->Abstraction.fo_rename   = &fbadop_rename;
    762 
    763         Stream->NumRead     = 0;
    764         Stream->NumWritten  = 0;
    765         Stream->UnGetKey    = CHAR_NULL;
    766 
    767         if(Stream->Dev == NULL) {
    768           continue;                 // No device for this stream.
    769         }
    770             ConNode[i] = __DevRegister(stdioNames[i], NULL, &da_ConOpen, Stream,
    771                                        1, sizeof(ConInstance), stdioFlags[i]);
    772         if(ConNode[i] == NULL) {
    773               Status = EFIerrno;    // Grab error code that DevRegister produced.
    774           break;
    775         }
    776         Stream->Parent = ConNode[i];
    777       }
    778       /* Initialize Ioctl flags until Ioctl is really implemented. */
    779       TtyCooked = TRUE;
    780       TtyEcho   = TRUE;
    781     }
    782   }
    783   return  Status;
    784 }
    785 
    786 RETURN_STATUS
    787 EFIAPI
    788 __Cons_deconstruct(
    789   IN EFI_HANDLE        ImageHandle,
    790   IN EFI_SYSTEM_TABLE  *SystemTable
    791 )
    792 {
    793   int   i;
    794 
    795   for(i = 0; i < NUM_SPECIAL; ++i) {
    796     if(ConNode[i] != NULL) {
    797       FreePool(ConNode[i]);
    798     }
    799   }
    800   if(ConInstanceList != NULL) {
    801     FreePool(ConInstanceList);
    802   }
    803   if(IIO != NULL) {
    804     IIO->Delete(IIO);
    805     IIO = NULL;
    806   }
    807 
    808   return RETURN_SUCCESS;
    809 }
    810 
    811 /* ######################################################################### */
    812 #if 0 /* Not implemented (yet?) for Console */
    813 
    814 static
    815 int
    816 EFIAPI
    817 da_ConCntl(
    818   struct __filedes *filp,
    819   UINT32,
    820   void *,
    821   void *
    822   )
    823 {
    824 }
    825 #endif  /* Not implemented for Console */
    826