]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Uefi/Devices/Console/daConsole.c
e9983e993399ca9aad6b2154cad85ad4c9e5393b
[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
25 #include <errno.h>
26 #include <wctype.h>
27 #include <wchar.h>
28 #include <stdarg.h>
29 #include <sys/fcntl.h>
30 #include <unistd.h>
31 #include <kfile.h>
32 #include <Device/Device.h>
33 #include <MainData.h>
34
35 static const CHAR16* const
36 stdioNames[NUM_SPECIAL] = {
37 L"stdin:", L"stdout:", L"stderr:"
38 };
39
40 static const int stdioFlags[NUM_SPECIAL] = {
41 O_RDONLY, // stdin
42 O_WRONLY, // stdout
43 O_WRONLY // stderr
44 };
45
46 static DeviceNode *ConNode[NUM_SPECIAL];
47 static ConInstance *ConInstanceList;
48
49 static wchar_t *ConReadBuf;
50
51 /* Flags settable by Ioctl */
52 static BOOLEAN TtyCooked;
53 static BOOLEAN TtyEcho;
54
55 ssize_t
56 WideTtyCvt( CHAR16 *dest, const char *buf, size_t n)
57 {
58 UINTN i;
59 wint_t wc;
60
61 for(i = 0; i < n; ++i) {
62 wc = btowc(*buf++);
63 if( wc == 0) {
64 break;
65 };
66 if(wc < 0) {
67 wc = BLOCKELEMENT_LIGHT_SHADE;
68 }
69 if(wc == L'\n') {
70 *dest++ = L'\r';
71 }
72 *dest++ = (CHAR16)wc;
73 }
74 *dest = 0;
75 return (ssize_t)i;
76 }
77
78 static
79 int
80 EFIAPI
81 da_ConClose(
82 IN struct __filedes *filp
83 )
84 {
85 ConInstance *Stream;
86
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
92 }
93 gMD->StdIo[Stream->InstanceNum] = NULL; // Mark the stream as closed
94 return RETURN_SUCCESS;
95 }
96
97 static
98 off_t
99 EFIAPI
100 da_ConSeek(
101 struct __filedes *filp,
102 off_t Position,
103 int whence ///< Ignored by Console
104 )
105 {
106 ConInstance *Stream;
107 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
108 XYoffset CursorPos;
109
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
115 }
116 if(Stream->InstanceNum == STDIN_FILENO) {
117 // Seek is not valid for stdin
118 EFIerrno = RETURN_UNSUPPORTED;
119 return -1;
120 }
121 // Everything is OK to do the final verification and "seek".
122 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
123 CursorPos.Offset = Position;
124
125 EFIerrno = Proto->SetCursorPosition(Proto,
126 (INTN)CursorPos.XYpos.Column,
127 (INTN)CursorPos.XYpos.Row);
128
129 if(RETURN_ERROR(EFIerrno)) {
130 return -1;
131 }
132 else {
133 return Position;
134 }
135 }
136
137 /* Write a NULL terminated WCS to the EFI console.
138
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
142
143 @return The number of characters written.
144 */
145 static
146 ssize_t
147 EFIAPI
148 da_ConWrite(
149 IN struct __filedes *filp,
150 IN off_t *Position,
151 IN size_t BufferSize,
152 IN const void *Buffer
153 )
154 {
155 EFI_STATUS Status;
156 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
157 ConInstance *Stream;
158 ssize_t NumChar;
159 //XYoffset CursorPos;
160
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
166 }
167 if(Stream->InstanceNum == STDIN_FILENO) {
168 // Write is not valid for stdin
169 EFIerrno = RETURN_UNSUPPORTED;
170 return -1;
171 }
172 // Everything is OK to do the write.
173 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
174
175 // Convert string from MBCS to WCS and translate \n to \r\n.
176 NumChar = WideTtyCvt(gMD->UString, (const char *)Buffer, BufferSize);
177 //if(NumChar > 0) {
178 // BufferSize = (size_t)(NumChar * sizeof(CHAR16));
179 //}
180 BufferSize = NumChar;
181
182 //if( Position != NULL) {
183 // CursorPos.Offset = (UINT64)*Position;
184
185 // Status = Proto->SetCursorPosition(Proto,
186 // (INTN)CursorPos.XYpos.Column,
187 // (INTN)CursorPos.XYpos.Row);
188 // if(RETURN_ERROR(Status)) {
189 // return -1;
190 // }
191 //}
192
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
198 }
199 else {
200 //BufferSize = NumChar;
201 Stream->NumWritten += NumChar;
202 }
203 EFIerrno = Status;
204 return BufferSize;
205 }
206
207 /** Read characters from the console input device.
208
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.
213
214 @return Number of bytes actually placed into Buffer.
215
216 @todo Handle encodings other than ASCII-7 and UEFI.
217 **/
218 static
219 ssize_t
220 EFIAPI
221 da_ConRead(
222 IN OUT struct __filedes *filp,
223 IN OUT off_t *offset, // Console ignores this
224 IN size_t BufferSize,
225 OUT VOID *Buffer
226 )
227 {
228 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
229 ConInstance *Stream;
230 wchar_t *OutPtr;
231 EFI_INPUT_KEY Key;
232 UINTN NumChar;
233 UINTN Edex;
234 EFI_STATUS Status = RETURN_SUCCESS;
235 UINTN i;
236 char EchoBuff[MB_CUR_MAX + 1];
237 int NumEcho;
238
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
244 }
245 if(Stream->InstanceNum != STDIN_FILENO) {
246 // Read only valid for stdin
247 EFIerrno = RETURN_UNSUPPORTED;
248 return -1;
249 }
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;
254 OutPtr = ConReadBuf;
255 NumChar = (BufferSize > MAX_INPUT)? MAX_INPUT : BufferSize;
256 i = 0;
257 do {
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) {
261 break;
262 }
263 Status = Proto->ReadKeyStroke(Proto, &Key);
264 if(Status != RETURN_SUCCESS) {
265 break;
266 }
267 }
268 else {
269 Key.ScanCode = Stream->UnGetKey.ScanCode;
270 Key.UnicodeChar = Stream->UnGetKey.UnicodeChar;
271 Stream->UnGetKey.ScanCode = SCAN_NULL;
272 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
273 }
274 if(Key.ScanCode == SCAN_NULL) {
275 NumEcho = 0;
276 if(TtyCooked && (Key.UnicodeChar == CHAR_CARRIAGE_RETURN)) {
277 *OutPtr++ = CHAR_LINEFEED;
278 NumEcho = wctomb(EchoBuff, CHAR_LINEFEED);
279 }
280 else {
281 *OutPtr++ = Key.UnicodeChar;
282 NumEcho = wctomb(EchoBuff, Key.UnicodeChar);
283 }
284 ++i;
285 EchoBuff[NumEcho] = 0; /* Terminate the Echo buffer */
286 if(TtyEcho) {
287 /* Echo the character just input */
288 da_ConWrite(&gMD->fdarray[STDOUT_FILENO], NULL, 2, EchoBuff);
289 }
290 }
291 if(iswcntrl(Key.UnicodeChar)) { // If a control character, or a scan code
292 break;
293 }
294 } while(i < NumChar);
295
296 *OutPtr = L'\0'; // Terminate the input buffer
297
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.
302 */
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. */
305
306 EFIerrno = Status;
307 return (ssize_t)NumEcho; // Will be 0 if we didn't get a key
308 }
309
310 /** Console-specific helper function for the fstat() function.
311
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
316
317 All other members of the stat structure are left unchanged.
318 **/
319 static
320 int
321 EFIAPI
322 da_ConStat(
323 struct __filedes *filp,
324 struct stat *Buffer,
325 void *Something
326 )
327 {
328 ConInstance *Stream;
329 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
330 XYoffset CursorPos;
331 INT32 OutMode;
332 UINTN ModeCol;
333 UINTN ModeRow;
334
335 // ConGetInfo
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'
339 (Buffer == NULL))
340 {
341 EFIerrno = RETURN_INVALID_PARAMETER;
342 return -1;
343 }
344 // All of our parameters are correct, so fill in the information.
345 Buffer->st_blksize = 1;
346
347 // ConGetPosition
348 if(Stream->InstanceNum == STDIN_FILENO) {
349 // This is stdin
350 Buffer->st_curpos = 0;
351 Buffer->st_size = (off_t)Stream->NumRead;
352 Buffer->st_physsize = 1;
353 }
354 else {
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;
360
361 OutMode = Proto->Mode->Mode;
362 EFIerrno = Proto->QueryMode(Proto, (UINTN)OutMode, &ModeCol, &ModeRow);
363 if(RETURN_ERROR(EFIerrno)) {
364 Buffer->st_physsize = 0;
365 }
366 else {
367 CursorPos.XYpos.Column = (UINT32)ModeCol;
368 CursorPos.XYpos.Row = (UINT32)ModeRow;
369 Buffer->st_physsize = (off_t)CursorPos.Offset;
370 }
371 }
372 return 0;
373 }
374
375 static
376 int
377 EFIAPI
378 da_ConIoctl(
379 struct __filedes *filp,
380 ULONGN cmd,
381 va_list argp
382 )
383 {
384 return -EPERM;
385 }
386
387 /** Open an abstract Console Device.
388 **/
389 int
390 EFIAPI
391 da_ConOpen(
392 DeviceNode *DevNode,
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
397 )
398 {
399 ConInstance *Stream;
400
401 if((filp == NULL) ||
402 (DevNode == NULL))
403 {
404 EFIerrno = RETURN_INVALID_PARAMETER;
405 errno = EINVAL;
406 return -1;
407 }
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;
412 errno = EINVAL;
413 return -1; // Looks like a bad This pointer
414 }
415 gMD->StdIo[Stream->InstanceNum] = Stream;
416 filp->f_iflags |= (S_IFREG | _S_IFCHR | _S_ICONSOLE);
417 filp->f_offset = 0;
418 filp->f_ops = &Stream->Abstraction;
419
420 return 0;
421 }
422
423 #include <sys/poll.h>
424 /* Returns a bit mask describing which operations could be completed immediately.
425
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.
429
430 */
431 static
432 short
433 EFIAPI
434 da_ConPoll(
435 struct __filedes *filp,
436 short events
437 )
438 {
439 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
440 ConInstance *Stream;
441 EFI_STATUS Status = RETURN_SUCCESS;
442 short RdyMask = 0;
443
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
449 }
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) {
456 RdyMask = POLLIN;
457 if(Stream->UnGetKey.UnicodeChar != CHAR_NULL) {
458 RdyMask |= POLLRDNORM;
459 }
460 }
461 else {
462 Stream->UnGetKey.ScanCode = SCAN_NULL;
463 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
464 }
465 }
466 }
467 else if(Stream->InstanceNum < NUM_SPECIAL) { // Not 0, is it 1 or 2?
468 // Only output is supported for this device
469 RdyMask = POLLOUT;
470 }
471 else {
472 RdyMask = POLLERR; // Not one of the standard streams
473 }
474 EFIerrno = Status;
475
476 return (RdyMask & (events | POLL_RETONLY));
477 }
478
479 /** Construct the Console stream devices: stdin, stdout, stderr.
480
481 Allocate the instance structure and populate it with the information for
482 each stream device.
483 **/
484 RETURN_STATUS
485 EFIAPI
486 __Cons_construct(
487 IN EFI_HANDLE ImageHandle,
488 IN EFI_SYSTEM_TABLE *SystemTable
489 )
490 {
491 ConInstance *Stream;
492 RETURN_STATUS Status = RETURN_SUCCESS;
493 int i;
494
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;
499 }
500
501 for( i = 0; i < NUM_SPECIAL; ++i) {
502 // Get pointer to instance.
503 Stream = &ConInstanceList[i];
504
505 Stream->Cookie = CON_COOKIE;
506 Stream->InstanceNum = i;
507
508 switch(i) {
509 case STDIN_FILENO:
510 Stream->Dev = SystemTable->ConIn;
511 break;
512 case STDOUT_FILENO:
513 Stream->Dev = SystemTable->ConOut;
514 break;
515 case STDERR_FILENO:
516 if(SystemTable->StdErr == NULL) {
517 Stream->Dev = SystemTable->ConOut;
518 }
519 else {
520 Stream->Dev = SystemTable->StdErr;
521 }
522 break;
523 default:
524 return RETURN_VOLUME_CORRUPTED; // This is a "should never happen" case.
525 }
526
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;
540
541 Stream->NumRead = 0;
542 Stream->NumWritten = 0;
543 Stream->UnGetKey.ScanCode = SCAN_NULL;
544 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
545
546 if(Stream->Dev == NULL) {
547 continue; // No device for this stream.
548 }
549 ConNode[i] = __DevRegister(stdioNames[i], NULL, &da_ConOpen, Stream, 1, sizeof(ConInstance), stdioFlags[i]);
550 if(ConNode[i] == NULL) {
551 Status = EFIerrno;
552 break;
553 }
554 Stream->Parent = ConNode[i];
555 }
556 /* Initialize Ioctl flags until Ioctl is really implemented. */
557 TtyCooked = TRUE;
558 TtyEcho = TRUE;
559
560 return Status;
561 }
562
563 RETURN_STATUS
564 EFIAPI
565 __Cons_deconstruct(
566 IN EFI_HANDLE ImageHandle,
567 IN EFI_SYSTEM_TABLE *SystemTable
568 )
569 {
570 int i;
571
572 for(i = 0; i < NUM_SPECIAL; ++i) {
573 if(ConNode[i] != NULL) {
574 FreePool(ConNode[i]);
575 }
576 }
577 if(ConInstanceList != NULL) {
578 FreePool(ConInstanceList);
579 }
580 if(ConReadBuf != NULL) {
581 FreePool(ConReadBuf);
582 }
583
584 return RETURN_SUCCESS;
585 }
586
587 /* ######################################################################### */
588 #if 0 /* Not implemented for Console */
589
590 static
591 int
592 EFIAPI
593 da_ConCntl(
594 struct __filedes *filp,
595 UINT32,
596 void *,
597 void *
598 )
599 {
600 }
601
602 static
603 int
604 EFIAPI
605 da_ConFlush(
606 struct __filedes *filp
607 )
608 {
609 return 0;
610 }
611 #endif /* Not implemented for Console */