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