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
WideTtyCvt(CHAR16 * dest,const char * buf,ssize_t n,mbstate_t * Cs)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
da_ConSeek(struct __filedes * filp,off_t Position,int whence)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
da_ConWrite(IN struct __filedes * filp,IN off_t * Position,IN size_t BufferSize,IN const void * Buffer)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
da_ConRawRead(IN OUT struct __filedes * filp,OUT wchar_t * Character)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
da_ConRead(IN OUT struct __filedes * filp,IN OUT off_t * offset,IN size_t BufferSize,OUT VOID * Buffer)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
da_ConStat(struct __filedes * filp,struct stat * Buffer,void * Something)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
da_ConIoctl(struct __filedes * filp,ULONGN cmd,va_list argp)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
da_ConOpen(DeviceNode * DevNode,struct __filedes * filp,int DevInstance,wchar_t * Path,wchar_t * MPath)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
da_ConFlush(struct __filedes * filp)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
da_ConClose(IN struct __filedes * filp)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
da_ConPoll(struct __filedes * filp,short events)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
__Cons_construct(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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
__Cons_deconstruct(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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