]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Uefi/Devices/Console/daConsole.c
Add Socket Libraries.
[mirror_edk2.git] / StdLib / LibC / Uefi / Devices / Console / daConsole.c
1 /** @file
2 Abstract device driver for the UEFI Console.
3
4 Manipulates abstractions for stdin, stdout, stderr.
5
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.
11
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.
14
15 **/
16 #include <Uefi.h>
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>
22
23 #include <LibConfig.h>
24 #include <sys/EfiSysCall.h>
25
26 #include <errno.h>
27 #include <wctype.h>
28 #include <wchar.h>
29 #include <sys/fcntl.h>
30 #include <kfile.h>
31 #include <Device/Device.h>
32 #include <MainData.h>
33
34 static const CHAR16* const
35 stdioNames[NUM_SPECIAL] = {
36 L"stdin:", L"stdout:", L"stderr:"
37 };
38
39 static const int stdioFlags[NUM_SPECIAL] = {
40 O_RDONLY, // stdin
41 O_WRONLY, // stdout
42 O_WRONLY // stderr
43 };
44
45 static DeviceNode *ConNode[NUM_SPECIAL];
46 static ConInstance *ConInstanceList;
47
48 static wchar_t *ConReadBuf;
49
50 /* Flags settable by Ioctl */
51 static BOOLEAN TtyCooked;
52 static BOOLEAN TtyEcho;
53
54 ssize_t
55 WideTtyCvt( CHAR16 *dest, const char *buf, size_t n)
56 {
57 UINTN i;
58 wint_t wc;
59
60 for(i = 0; i < n; ++i) {
61 wc = btowc(*buf++);
62 if( wc == 0) {
63 break;
64 };
65 if(wc < 0) {
66 wc = BLOCKELEMENT_LIGHT_SHADE;
67 }
68 if(wc == L'\n') {
69 *dest++ = L'\r';
70 }
71 *dest++ = (CHAR16)wc;
72 }
73 *dest = 0;
74 return (ssize_t)i;
75 }
76
77 static
78 int
79 EFIAPI
80 da_ConClose(
81 IN struct __filedes *filp
82 )
83 {
84 ConInstance *Stream;
85
86 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
87 // Quick check to see if Stream looks reasonable
88 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
89 EFIerrno = RETURN_INVALID_PARAMETER;
90 return -1; // Looks like a bad File Descriptor pointer
91 }
92 gMD->StdIo[Stream->InstanceNum] = NULL; // Mark the stream as closed
93 return RETURN_SUCCESS;
94 }
95
96 static
97 off_t
98 EFIAPI
99 da_ConSeek(
100 struct __filedes *filp,
101 off_t Position,
102 int whence ///< Ignored by Console
103 )
104 {
105 ConInstance *Stream;
106 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
107 XYoffset CursorPos;
108
109 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
110 // Quick check to see if Stream looks reasonable
111 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
112 EFIerrno = RETURN_INVALID_PARAMETER;
113 return -1; // Looks like a bad This pointer
114 }
115 if(Stream->InstanceNum == STDIN_FILENO) {
116 // Seek is not valid for stdin
117 EFIerrno = RETURN_UNSUPPORTED;
118 return -1;
119 }
120 // Everything is OK to do the final verification and "seek".
121 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
122 CursorPos.Offset = Position;
123
124 EFIerrno = Proto->SetCursorPosition(Proto,
125 (INTN)CursorPos.XYpos.Column,
126 (INTN)CursorPos.XYpos.Row);
127
128 if(RETURN_ERROR(EFIerrno)) {
129 return -1;
130 }
131 else {
132 return Position;
133 }
134 }
135
136 /* Write a NULL terminated WCS to the EFI console.
137
138 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
139 the string couldn't be displayed.
140 @param[in] Buffer The WCS string to be displayed
141
142 @return The number of characters written.
143 */
144 static
145 ssize_t
146 EFIAPI
147 da_ConWrite(
148 IN struct __filedes *filp,
149 IN off_t *Position,
150 IN size_t BufferSize,
151 IN const void *Buffer
152 )
153 {
154 EFI_STATUS Status;
155 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
156 ConInstance *Stream;
157 ssize_t NumChar;
158 //XYoffset CursorPos;
159
160 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
161 // Quick check to see if Stream looks reasonable
162 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
163 EFIerrno = RETURN_INVALID_PARAMETER;
164 return -1; // Looks like a bad This pointer
165 }
166 if(Stream->InstanceNum == STDIN_FILENO) {
167 // Write is not valid for stdin
168 EFIerrno = RETURN_UNSUPPORTED;
169 return -1;
170 }
171 // Everything is OK to do the write.
172 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
173
174 // Convert string from MBCS to WCS and translate \n to \r\n.
175 NumChar = WideTtyCvt(gMD->UString, (const char *)Buffer, BufferSize);
176 //if(NumChar > 0) {
177 // BufferSize = (size_t)(NumChar * sizeof(CHAR16));
178 //}
179 BufferSize = NumChar;
180
181 //if( Position != NULL) {
182 // CursorPos.Offset = (UINT64)*Position;
183
184 // Status = Proto->SetCursorPosition(Proto,
185 // (INTN)CursorPos.XYpos.Column,
186 // (INTN)CursorPos.XYpos.Row);
187 // if(RETURN_ERROR(Status)) {
188 // return -1;
189 // }
190 //}
191
192 // Send the Unicode buffer to the console
193 Status = Proto->OutputString( Proto, gMD->UString);
194 // Depending on status, update BufferSize and return
195 if(RETURN_ERROR(Status)) {
196 BufferSize = 0; // We don't really know how many characters made it out
197 }
198 else {
199 //BufferSize = NumChar;
200 Stream->NumWritten += NumChar;
201 }
202 EFIerrno = Status;
203 return BufferSize;
204 }
205
206 /** Read characters from the console input device.
207
208 @param[in,out] filp Pointer to file descriptor for this file.
209 @param[in,out] offset Ignored.
210 @param[in] BufferSize Buffer size, in bytes.
211 @param[out] Buffer Buffer in which to place the read characters.
212
213 @return Number of bytes actually placed into Buffer.
214
215 @todo Handle encodings other than ASCII-7 and UEFI.
216 **/
217 static
218 ssize_t
219 EFIAPI
220 da_ConRead(
221 IN OUT struct __filedes *filp,
222 IN OUT off_t *offset, // Console ignores this
223 IN size_t BufferSize,
224 OUT VOID *Buffer
225 )
226 {
227 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
228 ConInstance *Stream;
229 wchar_t *OutPtr;
230 EFI_INPUT_KEY Key;
231 UINTN NumChar;
232 UINTN Edex;
233 EFI_STATUS Status = RETURN_SUCCESS;
234 UINTN i;
235 char EchoBuff[MB_CUR_MAX + 1];
236 int NumEcho;
237
238 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
239 // Quick check to see if Stream looks reasonable
240 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
241 EFIerrno = RETURN_INVALID_PARAMETER;
242 return -1; // Looks like a bad This pointer
243 }
244 if(Stream->InstanceNum != STDIN_FILENO) {
245 // Read only valid for stdin
246 EFIerrno = RETURN_UNSUPPORTED;
247 return -1;
248 }
249 // It looks like things are OK for trying to read
250 // We will accumulate *BufferSize characters or until we encounter
251 // an "activation" character. Currently any control character.
252 Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
253 OutPtr = ConReadBuf;
254 NumChar = (BufferSize > MAX_INPUT)? MAX_INPUT : BufferSize;
255 i = 0;
256 do {
257 if((Stream->UnGetKey.UnicodeChar == CHAR_NULL) && (Stream->UnGetKey.ScanCode == SCAN_NULL)) {
258 Status = gBS->WaitForEvent( 1, &Proto->WaitForKey, &Edex);
259 if(Status != RETURN_SUCCESS) {
260 break;
261 }
262 Status = Proto->ReadKeyStroke(Proto, &Key);
263 if(Status != RETURN_SUCCESS) {
264 break;
265 }
266 }
267 else {
268 Key.ScanCode = Stream->UnGetKey.ScanCode;
269 Key.UnicodeChar = Stream->UnGetKey.UnicodeChar;
270 Stream->UnGetKey.ScanCode = SCAN_NULL;
271 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
272 }
273 if(Key.ScanCode == SCAN_NULL) {
274 NumEcho = 0;
275 if(TtyCooked && (Key.UnicodeChar == CHAR_CARRIAGE_RETURN)) {
276 *OutPtr++ = CHAR_LINEFEED;
277 NumEcho = wctomb(EchoBuff, CHAR_LINEFEED);
278 }
279 else {
280 *OutPtr++ = Key.UnicodeChar;
281 NumEcho = wctomb(EchoBuff, Key.UnicodeChar);
282 }
283 ++i;
284 EchoBuff[NumEcho] = 0; /* Terminate the Echo buffer */
285 if(TtyEcho) {
286 /* Echo the character just input */
287 da_ConWrite(&gMD->fdarray[STDOUT_FILENO], NULL, 2, EchoBuff);
288 }
289 }
290 if(iswcntrl(Key.UnicodeChar)) { // If a control character, or a scan code
291 break;
292 }
293 } while(i < NumChar);
294
295 *OutPtr = L'\0'; // Terminate the input buffer
296
297 /* Convert the input buffer and place in Buffer.
298 If the fully converted input buffer won't fit, write what will and
299 leave the rest in ConReadBuf with ConReadLeft indicating how many
300 unconverted characters remain in ConReadBuf.
301 */
302 NumEcho = (int)wcstombs(Buffer, ConReadBuf, BufferSize); /* Re-use NumEcho to hold number of bytes in Buffer */
303 /* More work needs to be done before locales other than C can be supported. */
304
305 EFIerrno = Status;
306 return (ssize_t)NumEcho; // Will be 0 if we didn't get a key
307 }
308
309 /** Console-specific helper function for the fstat() function.
310
311 st_size Set to number of characters read for stdin and number written for stdout and stderr.
312 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
313 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
314 st_blksize Set to 1 since this is a character device
315
316 All other members of the stat structure are left unchanged.
317 **/
318 static
319 int
320 EFIAPI
321 da_ConStat(
322 struct __filedes *filp,
323 struct stat *Buffer,
324 void *Something
325 )
326 {
327 ConInstance *Stream;
328 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
329 XYoffset CursorPos;
330 INT32 OutMode;
331 UINTN ModeCol;
332 UINTN ModeRow;
333
334 // ConGetInfo
335 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
336 // Quick check to see if Stream looks reasonable
337 if ((Stream->Cookie != CON_COOKIE) || // Cookie == 'IoAb'
338 (Buffer == NULL))
339 {
340 EFIerrno = RETURN_INVALID_PARAMETER;
341 return -1;
342 }
343 // All of our parameters are correct, so fill in the information.
344 Buffer->st_blksize = 1;
345
346 // ConGetPosition
347 if(Stream->InstanceNum == STDIN_FILENO) {
348 // This is stdin
349 Buffer->st_curpos = 0;
350 Buffer->st_size = (off_t)Stream->NumRead;
351 Buffer->st_physsize = 1;
352 }
353 else {
354 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
355 CursorPos.XYpos.Column = (UINT32)Proto->Mode->CursorColumn;
356 CursorPos.XYpos.Row = (UINT32)Proto->Mode->CursorRow;
357 Buffer->st_curpos = (off_t)CursorPos.Offset;
358 Buffer->st_size = (off_t)Stream->NumWritten;
359
360 OutMode = Proto->Mode->Mode;
361 EFIerrno = Proto->QueryMode(Proto, (UINTN)OutMode, &ModeCol, &ModeRow);
362 if(RETURN_ERROR(EFIerrno)) {
363 Buffer->st_physsize = 0;
364 }
365 else {
366 CursorPos.XYpos.Column = (UINT32)ModeCol;
367 CursorPos.XYpos.Row = (UINT32)ModeRow;
368 Buffer->st_physsize = (off_t)CursorPos.Offset;
369 }
370 }
371 return 0;
372 }
373
374 static
375 int
376 EFIAPI
377 da_ConIoctl(
378 struct __filedes *filp,
379 ULONGN cmd,
380 void *argp
381 )
382 {
383 return -EPERM;
384 }
385
386 /** Open an abstract Console Device.
387 **/
388 int
389 EFIAPI
390 da_ConOpen(
391 DeviceNode *DevNode,
392 struct __filedes *filp,
393 int DevInstance, // Not used for console devices
394 wchar_t *Path, // Not used for console devices
395 wchar_t *MPath // Not used for console devices
396 )
397 {
398 ConInstance *Stream;
399
400 if((filp == NULL) ||
401 (DevNode == NULL))
402 {
403 EFIerrno = RETURN_INVALID_PARAMETER;
404 return -1;
405 }
406 Stream = (ConInstance *)DevNode->InstanceList;
407 // Quick check to see if Stream looks reasonable
408 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
409 EFIerrno = RETURN_INVALID_PARAMETER;
410 return -1; // Looks like a bad This pointer
411 }
412 gMD->StdIo[Stream->InstanceNum] = Stream;
413 filp->f_iflags |= (S_IFREG | _S_IFCHR | _S_ICONSOLE);
414 filp->f_offset = 0;
415 filp->f_ops = &Stream->Abstraction;
416
417 return 0;
418 }
419
420 #include <sys/poll.h>
421 /* Returns a bit mask describing which operations could be completed immediately.
422
423 (POLLIN | POLLRDNORM) A Unicode character is available to read
424 (POLLIN) A ScanCode is ready.
425 (POLLOUT) The device is ready for output - always set on stdout and stderr.
426
427 */
428 static
429 short
430 EFIAPI
431 da_ConPoll(
432 struct __filedes *filp,
433 short events
434 )
435 {
436 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
437 ConInstance *Stream;
438 EFI_STATUS Status = RETURN_SUCCESS;
439 short RdyMask = 0;
440
441 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
442 // Quick check to see if Stream looks reasonable
443 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
444 EFIerrno = RETURN_INVALID_PARAMETER;
445 return POLLNVAL; // Looks like a bad filp pointer
446 }
447 if(Stream->InstanceNum == 0) {
448 // Only input is supported for this device
449 Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
450 if((Stream->UnGetKey.UnicodeChar == CHAR_NULL) && (Stream->UnGetKey.ScanCode == SCAN_NULL)) {
451 Status = Proto->ReadKeyStroke(Proto, &Stream->UnGetKey);
452 if(Status == RETURN_SUCCESS) {
453 RdyMask = POLLIN;
454 if(Stream->UnGetKey.UnicodeChar != CHAR_NULL) {
455 RdyMask |= POLLRDNORM;
456 }
457 }
458 else {
459 Stream->UnGetKey.ScanCode = SCAN_NULL;
460 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
461 }
462 }
463 }
464 else if(Stream->InstanceNum < NUM_SPECIAL) { // Not 0, is it 1 or 2?
465 // Only output is supported for this device
466 RdyMask = POLLOUT;
467 }
468 else {
469 RdyMask = POLLERR; // Not one of the standard streams
470 }
471 EFIerrno = Status;
472
473 return (RdyMask & (events | POLL_RETONLY));
474 }
475
476 /** Construct the Console stream devices: stdin, stdout, stderr.
477
478 Allocate the instance structure and populate it with the information for
479 each stream device.
480 **/
481 RETURN_STATUS
482 EFIAPI
483 __Cons_construct(
484 IN EFI_HANDLE ImageHandle,
485 IN EFI_SYSTEM_TABLE *SystemTable
486 )
487 {
488 ConInstance *Stream;
489 RETURN_STATUS Status = RETURN_SUCCESS;
490 int i;
491
492 ConInstanceList = (ConInstance *)AllocateZeroPool(NUM_SPECIAL * sizeof(ConInstance));
493 ConReadBuf = (wchar_t *)AllocateZeroPool((MAX_INPUT + 1) * sizeof(wchar_t));
494 if((ConInstanceList == NULL) || (ConReadBuf == NULL)) {
495 return RETURN_OUT_OF_RESOURCES;
496 }
497
498 for( i = 0; i < NUM_SPECIAL; ++i) {
499 // Get pointer to instance.
500 Stream = &ConInstanceList[i];
501
502 Stream->Cookie = CON_COOKIE;
503 Stream->InstanceNum = i;
504
505 switch(i) {
506 case STDIN_FILENO:
507 Stream->Dev = SystemTable->ConIn;
508 break;
509 case STDOUT_FILENO:
510 Stream->Dev = SystemTable->ConOut;
511 break;
512 case STDERR_FILENO:
513 if(SystemTable->StdErr == NULL) {
514 Stream->Dev = SystemTable->ConOut;
515 }
516 else {
517 Stream->Dev = SystemTable->StdErr;
518 }
519 break;
520 default:
521 return RETURN_VOLUME_CORRUPTED; // This is a "should never happen" case.
522 }
523
524 Stream->Abstraction.fo_close = &da_ConClose;
525 Stream->Abstraction.fo_read = &da_ConRead;
526 Stream->Abstraction.fo_write = &da_ConWrite;
527 Stream->Abstraction.fo_stat = &da_ConStat;
528 Stream->Abstraction.fo_lseek = &da_ConSeek;
529 Stream->Abstraction.fo_fcntl = &fnullop_fcntl;
530 Stream->Abstraction.fo_ioctl = &da_ConIoctl;
531 Stream->Abstraction.fo_poll = &da_ConPoll;
532 Stream->Abstraction.fo_flush = &fnullop_flush;
533 Stream->Abstraction.fo_delete = &fbadop_delete;
534 Stream->Abstraction.fo_mkdir = &fbadop_mkdir;
535 Stream->Abstraction.fo_rmdir = &fbadop_rmdir;
536 Stream->Abstraction.fo_rename = &fbadop_rename;
537
538 Stream->NumRead = 0;
539 Stream->NumWritten = 0;
540 Stream->UnGetKey.ScanCode = SCAN_NULL;
541 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
542
543 if(Stream->Dev == NULL) {
544 continue; // No device for this stream.
545 }
546 ConNode[i] = __DevRegister(stdioNames[i], NULL, &da_ConOpen, Stream, 1, sizeof(ConInstance), stdioFlags[i]);
547 if(ConNode[i] == NULL) {
548 Status = EFIerrno;
549 break;
550 }
551 Stream->Parent = ConNode[i];
552 }
553 /* Initialize Ioctl flags until Ioctl is really implemented. */
554 TtyCooked = TRUE;
555 TtyEcho = TRUE;
556
557 return Status;
558 }
559
560 RETURN_STATUS
561 EFIAPI
562 __Cons_deconstruct(
563 IN EFI_HANDLE ImageHandle,
564 IN EFI_SYSTEM_TABLE *SystemTable
565 )
566 {
567 int i;
568
569 for(i = 0; i < NUM_SPECIAL; ++i) {
570 if(ConNode[i] != NULL) {
571 FreePool(ConNode[i]);
572 }
573 }
574 if(ConInstanceList != NULL) {
575 FreePool(ConInstanceList);
576 }
577 if(ConReadBuf != NULL) {
578 FreePool(ConReadBuf);
579 }
580
581 return RETURN_SUCCESS;
582 }
583
584 /* ######################################################################### */
585 #if 0 /* Not implemented for Console */
586
587 static
588 int
589 EFIAPI
590 da_ConCntl(
591 struct __filedes *filp,
592 UINT32,
593 void *,
594 void *
595 )
596 {
597 }
598
599 static
600 int
601 EFIAPI
602 da_ConFlush(
603 struct __filedes *filp
604 )
605 {
606 return 0;
607 }
608 #endif /* Not implemented for Console */