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