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