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