2 Abstract device driver for the UEFI Console.
4 Manipulates abstractions for stdin, stdout, stderr.
6 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials are licensed and made available under
8 the terms and conditions of the BSD License that accompanies this distribution.
9 The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php.
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <Library/BaseLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Protocol/SimpleTextIn.h>
21 #include <Protocol/SimpleTextOut.h>
23 #include <LibConfig.h>
29 #include <sys/fcntl.h>
32 #include <Device/Device.h>
35 static const CHAR16
* const
36 stdioNames
[NUM_SPECIAL
] = {
37 L
"stdin:", L
"stdout:", L
"stderr:"
40 static const int stdioFlags
[NUM_SPECIAL
] = {
46 static DeviceNode
*ConNode
[NUM_SPECIAL
];
47 static ConInstance
*ConInstanceList
;
49 static wchar_t *ConReadBuf
;
51 /* Flags settable by Ioctl */
52 static BOOLEAN TtyCooked
;
53 static BOOLEAN TtyEcho
;
56 WideTtyCvt( CHAR16
*dest
, const char *buf
, size_t n
)
61 for(i
= 0; i
< n
; ++i
) {
67 wc
= BLOCKELEMENT_LIGHT_SHADE
;
82 IN
struct __filedes
*filp
87 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
88 // Quick check to see if Stream looks reasonable
89 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
90 EFIerrno
= RETURN_INVALID_PARAMETER
;
91 return -1; // Looks like a bad File Descriptor pointer
93 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
94 return RETURN_SUCCESS
;
101 struct __filedes
*filp
,
103 int whence
///< Ignored by Console
107 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
110 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
111 // Quick check to see if Stream looks reasonable
112 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
113 EFIerrno
= RETURN_INVALID_PARAMETER
;
114 return -1; // Looks like a bad This pointer
116 if(Stream
->InstanceNum
== STDIN_FILENO
) {
117 // Seek is not valid for stdin
118 EFIerrno
= RETURN_UNSUPPORTED
;
121 // Everything is OK to do the final verification and "seek".
122 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
123 CursorPos
.Offset
= Position
;
125 EFIerrno
= Proto
->SetCursorPosition(Proto
,
126 (INTN
)CursorPos
.XYpos
.Column
,
127 (INTN
)CursorPos
.XYpos
.Row
);
129 if(RETURN_ERROR(EFIerrno
)) {
137 /* Write a NULL terminated WCS to the EFI console.
139 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
140 the string couldn't be displayed.
141 @param[in] Buffer The WCS string to be displayed
143 @return The number of characters written.
149 IN
struct __filedes
*filp
,
151 IN
size_t BufferSize
,
152 IN
const void *Buffer
156 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
159 //XYoffset CursorPos;
161 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
162 // Quick check to see if Stream looks reasonable
163 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
164 EFIerrno
= RETURN_INVALID_PARAMETER
;
165 return -1; // Looks like a bad This pointer
167 if(Stream
->InstanceNum
== STDIN_FILENO
) {
168 // Write is not valid for stdin
169 EFIerrno
= RETURN_UNSUPPORTED
;
172 // Everything is OK to do the write.
173 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
175 // Convert string from MBCS to WCS and translate \n to \r\n.
176 NumChar
= WideTtyCvt(gMD
->UString
, (const char *)Buffer
, BufferSize
);
178 // BufferSize = (size_t)(NumChar * sizeof(CHAR16));
180 BufferSize
= NumChar
;
182 //if( Position != NULL) {
183 // CursorPos.Offset = (UINT64)*Position;
185 // Status = Proto->SetCursorPosition(Proto,
186 // (INTN)CursorPos.XYpos.Column,
187 // (INTN)CursorPos.XYpos.Row);
188 // if(RETURN_ERROR(Status)) {
193 // Send the Unicode buffer to the console
194 Status
= Proto
->OutputString( Proto
, gMD
->UString
);
195 // Depending on status, update BufferSize and return
196 if(RETURN_ERROR(Status
)) {
197 BufferSize
= 0; // We don't really know how many characters made it out
200 //BufferSize = NumChar;
201 Stream
->NumWritten
+= NumChar
;
207 /** Read characters from the console input device.
209 @param[in,out] filp Pointer to file descriptor for this file.
210 @param[in,out] offset Ignored.
211 @param[in] BufferSize Buffer size, in bytes.
212 @param[out] Buffer Buffer in which to place the read characters.
214 @return Number of bytes actually placed into Buffer.
216 @todo Handle encodings other than ASCII-7 and UEFI.
222 IN OUT
struct __filedes
*filp
,
223 IN OUT off_t
*offset
, // Console ignores this
224 IN
size_t BufferSize
,
228 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
234 EFI_STATUS Status
= RETURN_SUCCESS
;
236 char EchoBuff
[MB_CUR_MAX
+ 1];
239 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
240 // Quick check to see if Stream looks reasonable
241 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
242 EFIerrno
= RETURN_INVALID_PARAMETER
;
243 return -1; // Looks like a bad This pointer
245 if(Stream
->InstanceNum
!= STDIN_FILENO
) {
246 // Read only valid for stdin
247 EFIerrno
= RETURN_UNSUPPORTED
;
250 // It looks like things are OK for trying to read
251 // We will accumulate *BufferSize characters or until we encounter
252 // an "activation" character. Currently any control character.
253 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
255 NumChar
= (BufferSize
> MAX_INPUT
)? MAX_INPUT
: BufferSize
;
258 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
259 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
260 if(Status
!= RETURN_SUCCESS
) {
263 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
264 if(Status
!= RETURN_SUCCESS
) {
269 Key
.ScanCode
= Stream
->UnGetKey
.ScanCode
;
270 Key
.UnicodeChar
= Stream
->UnGetKey
.UnicodeChar
;
271 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
272 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
274 if(Key
.ScanCode
== SCAN_NULL
) {
276 if(TtyCooked
&& (Key
.UnicodeChar
== CHAR_CARRIAGE_RETURN
)) {
277 *OutPtr
++ = CHAR_LINEFEED
;
278 NumEcho
= wctomb(EchoBuff
, CHAR_LINEFEED
);
281 *OutPtr
++ = Key
.UnicodeChar
;
282 NumEcho
= wctomb(EchoBuff
, Key
.UnicodeChar
);
285 EchoBuff
[NumEcho
] = 0; /* Terminate the Echo buffer */
287 /* Echo the character just input */
288 da_ConWrite(&gMD
->fdarray
[STDOUT_FILENO
], NULL
, 2, EchoBuff
);
291 if(iswcntrl(Key
.UnicodeChar
)) { // If a control character, or a scan code
294 } while(i
< NumChar
);
296 *OutPtr
= L
'\0'; // Terminate the input buffer
298 /* Convert the input buffer and place in Buffer.
299 If the fully converted input buffer won't fit, write what will and
300 leave the rest in ConReadBuf with ConReadLeft indicating how many
301 unconverted characters remain in ConReadBuf.
303 NumEcho
= (int)wcstombs(Buffer
, ConReadBuf
, BufferSize
); /* Re-use NumEcho to hold number of bytes in Buffer */
304 /* More work needs to be done before locales other than C can be supported. */
307 return (ssize_t
)NumEcho
; // Will be 0 if we didn't get a key
310 /** Console-specific helper function for the fstat() function.
312 st_size Set to number of characters read for stdin and number written for stdout and stderr.
313 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
314 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
315 st_blksize Set to 1 since this is a character device
317 All other members of the stat structure are left unchanged.
323 struct __filedes
*filp
,
329 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
336 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
337 // Quick check to see if Stream looks reasonable
338 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
341 EFIerrno
= RETURN_INVALID_PARAMETER
;
344 // All of our parameters are correct, so fill in the information.
345 Buffer
->st_blksize
= 1;
348 if(Stream
->InstanceNum
== STDIN_FILENO
) {
350 Buffer
->st_curpos
= 0;
351 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
352 Buffer
->st_physsize
= 1;
355 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
356 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
357 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
358 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
359 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
361 OutMode
= Proto
->Mode
->Mode
;
362 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
363 if(RETURN_ERROR(EFIerrno
)) {
364 Buffer
->st_physsize
= 0;
367 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
368 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
369 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
379 struct __filedes
*filp
,
387 /** Open an abstract Console Device.
393 struct __filedes
*filp
,
394 int DevInstance
, // Not used for console devices
395 wchar_t *Path
, // Not used for console devices
396 wchar_t *MPath
// Not used for console devices
404 EFIerrno
= RETURN_INVALID_PARAMETER
;
408 Stream
= (ConInstance
*)DevNode
->InstanceList
;
409 // Quick check to see if Stream looks reasonable
410 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
411 EFIerrno
= RETURN_INVALID_PARAMETER
;
413 return -1; // Looks like a bad This pointer
415 gMD
->StdIo
[Stream
->InstanceNum
] = Stream
;
416 filp
->f_iflags
|= (S_IFREG
| _S_IFCHR
| _S_ICONSOLE
);
418 filp
->f_ops
= &Stream
->Abstraction
;
423 #include <sys/poll.h>
424 /* Returns a bit mask describing which operations could be completed immediately.
426 (POLLIN | POLLRDNORM) A Unicode character is available to read
427 (POLLIN) A ScanCode is ready.
428 (POLLOUT) The device is ready for output - always set on stdout and stderr.
435 struct __filedes
*filp
,
439 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
441 EFI_STATUS Status
= RETURN_SUCCESS
;
444 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
445 // Quick check to see if Stream looks reasonable
446 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
447 EFIerrno
= RETURN_INVALID_PARAMETER
;
448 return POLLNVAL
; // Looks like a bad filp pointer
450 if(Stream
->InstanceNum
== 0) {
451 // Only input is supported for this device
452 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
453 if((Stream
->UnGetKey
.UnicodeChar
== CHAR_NULL
) && (Stream
->UnGetKey
.ScanCode
== SCAN_NULL
)) {
454 Status
= Proto
->ReadKeyStroke(Proto
, &Stream
->UnGetKey
);
455 if(Status
== RETURN_SUCCESS
) {
457 if(Stream
->UnGetKey
.UnicodeChar
!= CHAR_NULL
) {
458 RdyMask
|= POLLRDNORM
;
462 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
463 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
467 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
468 // Only output is supported for this device
472 RdyMask
= POLLERR
; // Not one of the standard streams
476 return (RdyMask
& (events
| POLL_RETONLY
));
479 /** Construct the Console stream devices: stdin, stdout, stderr.
481 Allocate the instance structure and populate it with the information for
487 IN EFI_HANDLE ImageHandle
,
488 IN EFI_SYSTEM_TABLE
*SystemTable
492 RETURN_STATUS Status
= RETURN_SUCCESS
;
495 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
496 ConReadBuf
= (wchar_t *)AllocateZeroPool((MAX_INPUT
+ 1) * sizeof(wchar_t));
497 if((ConInstanceList
== NULL
) || (ConReadBuf
== NULL
)) {
498 return RETURN_OUT_OF_RESOURCES
;
501 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
502 // Get pointer to instance.
503 Stream
= &ConInstanceList
[i
];
505 Stream
->Cookie
= CON_COOKIE
;
506 Stream
->InstanceNum
= i
;
510 Stream
->Dev
= SystemTable
->ConIn
;
513 Stream
->Dev
= SystemTable
->ConOut
;
516 if(SystemTable
->StdErr
== NULL
) {
517 Stream
->Dev
= SystemTable
->ConOut
;
520 Stream
->Dev
= SystemTable
->StdErr
;
524 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
527 Stream
->Abstraction
.fo_close
= &da_ConClose
;
528 Stream
->Abstraction
.fo_read
= &da_ConRead
;
529 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
530 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
531 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
532 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
533 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
534 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
535 Stream
->Abstraction
.fo_flush
= &fnullop_flush
;
536 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
537 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
538 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
539 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
542 Stream
->NumWritten
= 0;
543 Stream
->UnGetKey
.ScanCode
= SCAN_NULL
;
544 Stream
->UnGetKey
.UnicodeChar
= CHAR_NULL
;
546 if(Stream
->Dev
== NULL
) {
547 continue; // No device for this stream.
549 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
, 1, sizeof(ConInstance
), stdioFlags
[i
]);
550 if(ConNode
[i
] == NULL
) {
554 Stream
->Parent
= ConNode
[i
];
556 /* Initialize Ioctl flags until Ioctl is really implemented. */
566 IN EFI_HANDLE ImageHandle
,
567 IN EFI_SYSTEM_TABLE
*SystemTable
572 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
573 if(ConNode
[i
] != NULL
) {
574 FreePool(ConNode
[i
]);
577 if(ConInstanceList
!= NULL
) {
578 FreePool(ConInstanceList
);
580 if(ConReadBuf
!= NULL
) {
581 FreePool(ConReadBuf
);
584 return RETURN_SUCCESS
;
587 /* ######################################################################### */
588 #if 0 /* Not implemented for Console */
594 struct __filedes
*filp
,
606 struct __filedes
*filp
611 #endif /* Not implemented for Console */