]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Uefi/Console.c
Standard Libraries for EDK II.
[mirror_edk2.git] / StdLib / LibC / Uefi / Console.c
1 /** @file
2 File abstractions of the console.
3
4 Manipulates EFI_FILE_PROTOCOL abstractions for stdin, stdout, stderr.
5
6 Copyright (c) 2010, 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/BaseMemoryLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21
22 #include <LibConfig.h>
23 #include <sys/EfiCdefs.h>
24
25 #include <stdio.h>
26 #include <sys/fcntl.h>
27 #include <MainData.h>
28 #include <Efi/Console.h>
29
30 static const CHAR16 * stdioNames[NUM_SPECIAL] = {
31 L"stdin:", L"stdout:", L"stderr:"
32 };
33 static const int stdioFlags[NUM_SPECIAL] = {
34 O_RDONLY, // stdin
35 O_WRONLY, // stdout
36 O_WRONLY // stderr
37 };
38
39 static const int stdioMode[NUM_SPECIAL] = {
40 0444, // stdin
41 0222, // stdout
42 0222 // stderr
43 };
44
45 static
46 EFI_STATUS
47 EFIAPI
48 ConClose(
49 IN EFI_FILE_PROTOCOL *This
50 )
51 {
52 ConInstance *Stream;
53
54 Stream = BASE_CR(This, ConInstance, Abstraction);
55 // Quick check to see if Stream looks reasonable
56 if(Stream->Cookie != 0x62416F49) { // Cookie == 'IoAb'
57 return RETURN_INVALID_PARAMETER; // Looks like a bad This pointer
58 }
59 // Nothing to Flush.
60 // Mark the Stream as closed.
61 Stream->Dev = NULL;
62
63 return RETURN_SUCCESS;
64 }
65
66 static
67 EFI_STATUS
68 EFIAPI
69 ConDelete(
70 IN EFI_FILE_PROTOCOL *This
71 )
72 {
73 return RETURN_UNSUPPORTED;
74 }
75
76 static
77 EFI_STATUS
78 EFIAPI
79 ConRead(
80 IN EFI_FILE_PROTOCOL *This,
81 IN OUT UINTN *BufferSize,
82 OUT VOID *Buffer
83 )
84 {
85 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
86 ConInstance *Stream;
87 CHAR16 *OutPtr;
88 EFI_INPUT_KEY Key;
89 UINTN NumChar;
90 UINTN Edex;
91 EFI_STATUS Status;
92 UINTN i;
93
94 Stream = BASE_CR(This, ConInstance, Abstraction);
95 // Quick check to see if Stream looks reasonable
96 if(Stream->Cookie != 0x62416F49) { // Cookie == 'IoAb'
97 return RETURN_INVALID_PARAMETER; // Looks like a bad This pointer
98 }
99 if(Stream->Dev == NULL) {
100 // Can't read from an unopened Stream
101 return RETURN_DEVICE_ERROR;
102 }
103 if(Stream != &gMD->StdIo[0]) {
104 // Read only valid for stdin
105 return RETURN_UNSUPPORTED;
106 }
107 // It looks like things are OK for trying to read
108 // We will accumulate *BufferSize characters or until we encounter
109 // an "activation" character. Currently any control character.
110 Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
111 OutPtr = Buffer;
112 NumChar = (*BufferSize - 1) / sizeof(CHAR16);
113 i = 0;
114 do {
115 Status = gBS->WaitForEvent( 1, &Proto->WaitForKey, &Edex);
116 if(Status != EFI_SUCCESS) {
117 break;
118 }
119 Status = Proto->ReadKeyStroke(Proto, &Key);
120 if(Status != EFI_SUCCESS) {
121 break;
122 }
123 if(Key.ScanCode == SCAN_NULL) {
124 *OutPtr++ = Key.UnicodeChar;
125 ++i;
126 }
127 if(Key.UnicodeChar < 0x0020) { // If a control character, or a scan code
128 break;
129 }
130 } while(i < NumChar);
131 *BufferSize = i * sizeof(CHAR16);
132 return Status;
133 }
134
135 /* Write a NULL terminated WCS to the EFI console.
136
137 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
138 the string couldn't be displayed.
139 @parem[in] Buffer The WCS string to be displayed
140
141 */
142 static
143 EFI_STATUS
144 EFIAPI
145 ConWrite(
146 IN EFI_FILE_PROTOCOL *This,
147 IN OUT UINTN *BufferSize,
148 IN VOID *Buffer
149 )
150 {
151 EFI_STATUS Status;
152 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
153 ConInstance *Stream;
154 CHAR16 *MyBuf;
155 UINTN NumChar;
156
157 Stream = BASE_CR(This, ConInstance, Abstraction);
158 // Quick check to see if Stream looks reasonable
159 if(Stream->Cookie != 0x62416F49) { // Cookie == 'IoAb'
160 return RETURN_INVALID_PARAMETER; // Looks like a bad This pointer
161 }
162 if(Stream->Dev == NULL) {
163 // Can't write to an unopened Stream
164 return RETURN_DEVICE_ERROR;
165 }
166 if(Stream == &gMD->StdIo[0]) {
167 // Write is not valid for stdin
168 return RETURN_UNSUPPORTED;
169 }
170 // Everything is OK to do the write.
171 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
172 MyBuf = (CHAR16 *)Buffer;
173 NumChar = *BufferSize;
174
175 // Send MyBuf to the console
176 Status = Proto->OutputString( Proto, MyBuf);
177 // Depending on status, update BufferSize and return
178 if(Status != EFI_SUCCESS) {
179 *BufferSize = 0; // We don't really know how many characters made it out
180 }
181 else {
182 *BufferSize = NumChar;
183 Stream->NumWritten += NumChar;
184 }
185 return Status;
186 }
187
188 static
189 EFI_STATUS
190 EFIAPI
191 ConGetPosition(
192 IN EFI_FILE_PROTOCOL *This,
193 OUT UINT64 *Position
194 )
195 {
196 ConInstance *Stream;
197 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
198 XYoffset CursorPos;
199
200 Stream = BASE_CR(This, ConInstance, Abstraction);
201 // Quick check to see if Stream looks reasonable
202 if(Stream->Cookie != 0x62416F49) { // Cookie == 'IoAb'
203 return RETURN_INVALID_PARAMETER; // Looks like a bad This pointer
204 }
205 if(Stream == &gMD->StdIo[0]) {
206 // This is stdin
207 *Position = Stream->NumRead;
208 }
209 else {
210 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
211 CursorPos.XYpos.Column = (UINT32)Proto->Mode->CursorColumn;
212 CursorPos.XYpos.Row = (UINT32)Proto->Mode->CursorRow;
213 *Position = CursorPos.Offset;
214 }
215 return RETURN_SUCCESS;
216 }
217
218 static
219 EFI_STATUS
220 EFIAPI
221 ConSetPosition(
222 IN EFI_FILE_PROTOCOL *This,
223 IN UINT64 Position
224 )
225 {
226 ConInstance *Stream;
227 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
228 XYoffset CursorPos;
229
230 Stream = BASE_CR(This, ConInstance, Abstraction);
231 // Quick check to see if Stream looks reasonable
232 if(Stream->Cookie != 0x62416F49) { // Cookie == 'IoAb'
233 return RETURN_INVALID_PARAMETER; // Looks like a bad This pointer
234 }
235 if(Stream->Dev == NULL) {
236 // Can't write to an unopened Stream
237 return RETURN_DEVICE_ERROR;
238 }
239 if(Stream == &gMD->StdIo[0]) {
240 // Seek is not valid for stdin
241 return RETURN_UNSUPPORTED;
242 }
243 // Everything is OK to do the final verification and "seek".
244 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
245 CursorPos.Offset = Position;
246
247 return Proto->SetCursorPosition(Proto,
248 (INTN)CursorPos.XYpos.Column,
249 (INTN)CursorPos.XYpos.Row);
250 }
251
252 static
253 EFI_STATUS
254 EFIAPI
255 ConGetInfo(
256 IN EFI_FILE_PROTOCOL *This,
257 IN EFI_GUID *InformationType,
258 IN OUT UINTN *BufferSize,
259 OUT VOID *Buffer
260 )
261 {
262 EFI_FILE_INFO *InfoBuf;
263 ConInstance *Stream;
264
265 Stream = BASE_CR(This, ConInstance, Abstraction);
266 // Quick check to see if Stream looks reasonable
267 if((Stream->Cookie != 0x62416F49) || // Cookie == 'IoAb'
268 (Buffer == NULL))
269 {
270 return RETURN_INVALID_PARAMETER;
271 }
272 if(*BufferSize < sizeof(EFI_FILE_INFO)) {
273 *BufferSize = sizeof(EFI_FILE_INFO);
274 return RETURN_BUFFER_TOO_SMALL;
275 }
276 // All of our parameters are correct, so fill in the information.
277 (void) ZeroMem(Buffer, sizeof(EFI_FILE_INFO));
278 InfoBuf = (EFI_FILE_INFO *)Buffer;
279 InfoBuf->Size = sizeof(EFI_FILE_INFO);
280 InfoBuf->FileSize = 1;
281 InfoBuf->PhysicalSize = 1;
282 *BufferSize = sizeof(EFI_FILE_INFO);
283
284 return RETURN_SUCCESS;
285 }
286
287 static
288 EFI_STATUS
289 EFIAPI
290 ConSetInfo(
291 IN EFI_FILE_PROTOCOL *This,
292 IN EFI_GUID *InformationType,
293 IN UINTN BufferSize,
294 IN VOID *Buffer
295 )
296 {
297 return RETURN_UNSUPPORTED;
298 }
299
300 static
301 EFI_STATUS
302 EFIAPI
303 ConFlush(
304 IN EFI_FILE_PROTOCOL *This
305 )
306 {
307 return RETURN_SUCCESS;
308 }
309
310 EFI_STATUS
311 EFIAPI
312 ConOpen(
313 IN EFI_FILE_PROTOCOL *This,
314 OUT EFI_FILE_PROTOCOL **NewHandle,
315 IN CHAR16 *FileName,
316 IN UINT64 OpenMode, // Ignored
317 IN UINT64 Attributes // Ignored
318 )
319 {
320 ConInstance *Stream;
321 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
322 UINTN Columns;
323 UINTN Rows;
324 int i;
325
326 if((NewHandle == NULL) ||
327 (FileName == NULL))
328 {
329 return RETURN_INVALID_PARAMETER;
330 }
331 // Process FileName
332 for(i = 0; i < NUM_SPECIAL; ++i) {
333 if(StrCmp( stdioNames[i], FileName) == 0) {
334 break;
335 }
336 }
337 if(i >= NUM_SPECIAL) {
338 return RETURN_NO_MAPPING;
339 }
340 // Get pointer to instance.
341 Stream = &gMD->StdIo[i];
342 if(Stream->Dev == NULL) {
343 // If this stream has been closed, then
344 // Initialize instance.
345 Stream->Cookie = 0x62416F49;
346 Stream->NumRead = 0;
347 Stream->NumWritten = 0;
348 switch(i) {
349 case 0:
350 Stream->Dev = gST->ConIn;
351 break;
352 case 1:
353 Stream->Dev = gST->ConOut;
354 break;
355 case 2:
356 if(gST->StdErr == NULL) {
357 Stream->Dev = gST->ConOut;
358 }
359 else {
360 Stream->Dev = gST->StdErr;
361 }
362 break;
363 default:
364 return RETURN_VOLUME_CORRUPTED; // This is a "should never happen" case.
365 }
366 Stream->Abstraction.Revision = 0x00010000;
367 Stream->Abstraction.Open = &ConOpen;
368 Stream->Abstraction.Close = &ConClose;
369 Stream->Abstraction.Delete = &ConDelete;
370 Stream->Abstraction.Read = &ConRead;
371 Stream->Abstraction.Write = &ConWrite;
372 Stream->Abstraction.GetPosition = &ConGetPosition;
373 Stream->Abstraction.SetPosition = &ConSetPosition;
374 Stream->Abstraction.GetInfo = &ConGetInfo;
375 Stream->Abstraction.SetInfo = &ConSetInfo;
376 Stream->Abstraction.Flush = &ConFlush;
377 // Get additional information if this is an Output stream
378 if(i != 0) {
379 Proto = Stream->Dev;
380 Stream->ConOutMode = Proto->Mode->Mode;
381 if( Proto->QueryMode(Proto, Stream->ConOutMode, &Columns, &Rows) != RETURN_SUCCESS) {
382 Stream->Dev = NULL; // Mark this stream as closed
383 return RETURN_INVALID_PARAMETER;
384 }
385 Stream->MaxConXY.XYpos.Column = (UINT32)Columns;
386 Stream->MaxConXY.XYpos.Row = (UINT32)Rows;
387 }
388 }
389 // Save NewHandle and return.
390 *NewHandle = &Stream->Abstraction;
391
392 return RETURN_SUCCESS;
393 }