]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterGraphics.c
1. Sync the latest network stack. Add NetLibCreateIPv4DPathNode () in netlib library.
[mirror_edk2.git] / MdeModulePkg / Universal / Console / ConSplitterDxe / ConSplitterGraphics.c
CommitLineData
95276127 1/*++\r
2\r
3Copyright (c) 2006 - 2007, Intel Corporation\r
4All rights reserved. This program and the accompanying materials\r
5are licensed and made available under the terms and conditions of the BSD License\r
6which accompanies this distribution. The full text of the license may be found at\r
7http://opensource.org/licenses/bsd-license.php\r
8\r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11\r
12Module Name:\r
13\r
14 ConSplitterGraphics.c\r
15\r
16Abstract:\r
17\r
18 Support for ConsoleControl protocol. Support for UGA Draw spliter.\r
19 Support for DevNull Console Out. This console uses memory buffers\r
20 to represnt the console. It allows a console to start very early and\r
21 when a new console is added it is synced up with the current console\r
22\r
23--*/\r
24\r
95276127 25#include "ConSplitter.h"\r
26\r
95276127 27\r
28static CHAR16 mCrLfString[3] = { CHAR_CARRIAGE_RETURN, CHAR_LINEFEED, CHAR_NULL };\r
29\r
30EFI_STATUS\r
31EFIAPI\r
32ConSpliterConsoleControlGetMode (\r
33 IN EFI_CONSOLE_CONTROL_PROTOCOL *This,\r
34 OUT EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,\r
35 OUT BOOLEAN *GopExists,\r
36 OUT BOOLEAN *StdInLocked\r
37 )\r
38/*++\r
39\r
40 Routine Description:\r
41 Return the current video mode information. Also returns info about existence\r
42 of UGA Draw devices in system, and if the Std In device is locked. All the\r
43 arguments are optional and only returned if a non NULL pointer is passed in.\r
44\r
45 Arguments:\r
46 This - Protocol instance pointer.\r
47 Mode - Are we in text of grahics mode.\r
48 UgaExists - TRUE if UGA Spliter has found a UGA device\r
49 StdInLocked - TRUE if StdIn device is keyboard locked\r
50\r
51 Returns:\r
52 EFI_SUCCESS - Mode information returned.\r
53 EFI_INVALID_PARAMETER - Invalid parameters.\r
54\r
55--*/\r
56{\r
57 TEXT_OUT_SPLITTER_PRIVATE_DATA *Private;\r
58 UINTN Index;\r
59\r
60 Private = CONSOLE_CONTROL_SPLITTER_PRIVATE_DATA_FROM_THIS (This);\r
61\r
62 if (Mode == NULL) {\r
63 return EFI_INVALID_PARAMETER;\r
64 }\r
65\r
66 *Mode = Private->ConsoleOutputMode;\r
67\r
68 if (GopExists != NULL) {\r
69 *GopExists = FALSE;\r
70 for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) {\r
71 if ((Private->TextOutList[Index].GraphicsOutput != NULL) || (Private->TextOutList[Index].UgaDraw != NULL)) {\r
72 *GopExists = TRUE;\r
73 break;\r
74 }\r
75 }\r
76 }\r
77\r
78 if (StdInLocked != NULL) {\r
79 *StdInLocked = ConSpliterConssoleControlStdInLocked ();\r
80 }\r
81\r
82 return EFI_SUCCESS;\r
83}\r
84\r
85EFI_STATUS\r
86EFIAPI\r
87ConSpliterConsoleControlSetMode (\r
88 IN EFI_CONSOLE_CONTROL_PROTOCOL *This,\r
89 IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode\r
90 )\r
91/*++\r
92\r
93 Routine Description:\r
94 Set the current mode to either text or graphics. Graphics is\r
95 for Quiet Boot.\r
96\r
97 Arguments:\r
98 This - Protocol instance pointer.\r
99 Mode - Mode to set the\r
100\r
101 Returns:\r
102 EFI_SUCCESS - Mode information returned.\r
103 EFI_INVALID_PARAMETER - Invalid parameter.\r
104 EFI_UNSUPPORTED - Operation unsupported.\r
105\r
106--*/\r
107{\r
108 TEXT_OUT_SPLITTER_PRIVATE_DATA *Private;\r
109 UINTN Index;\r
110 TEXT_OUT_AND_GOP_DATA *TextAndGop;\r
111 BOOLEAN Supported;\r
112\r
113 Private = CONSOLE_CONTROL_SPLITTER_PRIVATE_DATA_FROM_THIS (This);\r
114\r
115 if (Mode >= EfiConsoleControlScreenMaxValue) {\r
116 return EFI_INVALID_PARAMETER;\r
117 }\r
118\r
119 //\r
120 // Judge current mode with wanted mode at first.\r
121 //\r
122 if (Private->ConsoleOutputMode == Mode) {\r
123 return EFI_SUCCESS;\r
124 }\r
125\r
126 Supported = FALSE;\r
127 TextAndGop = &Private->TextOutList[0];\r
128 for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++, TextAndGop++) {\r
129 if ((TextAndGop->GraphicsOutput != NULL) || (TextAndGop->UgaDraw != NULL)) {\r
130 Supported = TRUE;\r
131 break;\r
132 }\r
133 }\r
134\r
135 if ((!Supported) && (Mode == EfiConsoleControlScreenGraphics)) {\r
136 return EFI_UNSUPPORTED;\r
137 }\r
138\r
139 Private->ConsoleOutputMode = Mode;\r
140\r
141 TextAndGop = &Private->TextOutList[0];\r
142 for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++, TextAndGop++) {\r
143\r
144 TextAndGop->TextOutEnabled = TRUE;\r
145 //\r
146 // If we are going into Graphics mode disable ConOut to any UGA device\r
147 //\r
148 if ((Mode == EfiConsoleControlScreenGraphics) &&((TextAndGop->GraphicsOutput != NULL) || (TextAndGop->UgaDraw != NULL))) {\r
149 TextAndGop->TextOutEnabled = FALSE;\r
150 DevNullGopSync (Private, TextAndGop->GraphicsOutput, TextAndGop->UgaDraw);\r
151 }\r
152 }\r
153\r
154 if (Mode == EfiConsoleControlScreenText) {\r
155 DevNullSyncGopStdOut (Private);\r
156 }\r
157\r
158 return EFI_SUCCESS;\r
159}\r
160\r
161EFI_STATUS\r
162EFIAPI\r
163ConSpliterGraphicsOutputQueryMode (\r
164 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
165 IN UINT32 ModeNumber,\r
166 OUT UINTN *SizeOfInfo,\r
167 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info\r
168 )\r
169/*++\r
170\r
171 Routine Description:\r
172 Return the current video mode information.\r
173\r
174 Arguments:\r
175 This - Protocol instance pointer.\r
176 ModeNumber - The mode number to return information on.\r
177 Info - Caller allocated buffer that returns information about ModeNumber.\r
178 SizeOfInfo - A pointer to the size, in bytes, of the Info buffer.\r
179\r
180 Returns:\r
181 EFI_SUCCESS - Mode information returned.\r
182 EFI_BUFFER_TOO_SMALL - The Info buffer was too small.\r
183 EFI_DEVICE_ERROR - A hardware error occurred trying to retrieve the video mode.\r
184 EFI_NOT_STARTED - Video display is not initialized. Call SetMode ()\r
185 EFI_INVALID_PARAMETER - One of the input args was NULL.\r
186\r
187--*/\r
188{\r
189 TEXT_OUT_SPLITTER_PRIVATE_DATA *Private;\r
190 TEXT_OUT_GOP_MODE *Mode;\r
191\r
192 if (This == NULL || Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) {\r
193 return EFI_INVALID_PARAMETER;\r
194 }\r
195\r
196 //\r
197 // retrieve private data\r
198 //\r
199 Private = GRAPHICS_OUTPUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This);\r
200\r
201 if (Private->HardwareNeedsStarting) {\r
202 return EFI_NOT_STARTED;\r
203 }\r
204\r
205 *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));\r
206\r
207 if (*Info == NULL) {\r
208 return EFI_OUT_OF_RESOURCES;\r
209 }\r
210\r
211 *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);\r
212\r
213 CopyMem (*Info, Private->GraphicsOutput.Mode->Info, *SizeOfInfo);\r
214 Mode = &Private->GraphicsOutputModeBuffer[ModeNumber];\r
215 (*Info)->HorizontalResolution = Mode->HorizontalResolution;\r
216 (*Info)->VerticalResolution = Mode->VerticalResolution;\r
217 (*Info)->PixelsPerScanLine = Mode->HorizontalResolution;\r
218\r
219 return EFI_SUCCESS;\r
220}\r
221\r
222EFI_STATUS\r
223EFIAPI\r
224ConSpliterGraphicsOutputSetMode (\r
225 IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This,\r
226 IN UINT32 ModeNumber\r
227 )\r
228/*++\r
229\r
230Routine Description:\r
231\r
232 Graphics output protocol interface to set video mode\r
233\r
234 Arguments:\r
235 This - Protocol instance pointer.\r
236 ModeNumber - The mode number to be set.\r
237\r
238 Returns:\r
239 EFI_SUCCESS - Graphics mode was changed.\r
240 EFI_DEVICE_ERROR - The device had an error and could not complete the request.\r
241 EFI_UNSUPPORTED - ModeNumber is not supported by this device.\r
242\r
243--*/\r
244{\r
245 EFI_STATUS Status;\r
246 TEXT_OUT_SPLITTER_PRIVATE_DATA *Private;\r
247 UINTN Index;\r
248 EFI_STATUS ReturnStatus;\r
249 TEXT_OUT_GOP_MODE *Mode;\r
250 UINTN Size;\r
251 EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;\r
252 UINTN NumberIndex;\r
253 UINTN SizeOfInfo;\r
254 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;\r
255 EFI_UGA_DRAW_PROTOCOL *UgaDraw;\r
256\r
257 if (ModeNumber >= This->Mode->MaxMode) {\r
258 return EFI_UNSUPPORTED;\r
259 }\r
260\r
261 if (ModeNumber == This->Mode->Mode) {\r
262 return EFI_SUCCESS;\r
263 }\r
264\r
265 Private = GRAPHICS_OUTPUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This);\r
266\r
267 //\r
268 // GopDevNullSetMode ()\r
269 //\r
270 ReturnStatus = EFI_SUCCESS;\r
271\r
272 //\r
273 // Free the old version\r
274 //\r
275 if (Private->GraphicsOutputBlt != NULL) {\r
276 FreePool (Private->GraphicsOutputBlt);\r
277 }\r
278\r
279 //\r
280 // Allocate the virtual Blt buffer\r
281 //\r
282 Mode = &Private->GraphicsOutputModeBuffer[ModeNumber];\r
283 Size = Mode->HorizontalResolution * Mode->VerticalResolution * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);\r
284 Private->GraphicsOutputBlt = AllocateZeroPool (Size);\r
285\r
286 if (Private->GraphicsOutputBlt == NULL) {\r
287 return EFI_OUT_OF_RESOURCES;\r
288 }\r
289\r
290 if (!Private->HardwareNeedsStarting) {\r
291 if (Private->ConsoleOutputMode != EfiConsoleControlScreenGraphics) {\r
292 return EFI_UNSUPPORTED;\r
293 }\r
294 }\r
295 //\r
296 // return the worst status met\r
297 //\r
298 for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) {\r
299 GraphicsOutput = Private->TextOutList[Index].GraphicsOutput;\r
300 if (GraphicsOutput != NULL) {\r
301 //\r
302 // Find corresponding ModeNumber of this GraphicsOutput instance\r
303 //\r
304 for (NumberIndex = 0; NumberIndex < GraphicsOutput->Mode->MaxMode; NumberIndex ++) {\r
305 Status = GraphicsOutput->QueryMode (GraphicsOutput, (UINT32) NumberIndex, &SizeOfInfo, &Info);\r
306 if (EFI_ERROR (Status)) {\r
307 return Status;\r
308 }\r
309 if ((Info->HorizontalResolution == Mode->HorizontalResolution) && (Info->VerticalResolution == Mode->VerticalResolution)) {\r
310 FreePool (Info);\r
311 break;\r
312 }\r
313 FreePool (Info);\r
314 }\r
315\r
316 Status = GraphicsOutput->SetMode (GraphicsOutput, (UINT32) NumberIndex);\r
317 if (EFI_ERROR (Status)) {\r
318 ReturnStatus = Status;\r
319 }\r
320 }\r
321\r
322 UgaDraw = Private->TextOutList[Index].UgaDraw;\r
323 if (UgaDraw != NULL) {\r
324 Status = UgaDraw->SetMode (\r
325 UgaDraw,\r
326 Mode->HorizontalResolution,\r
327 Mode->VerticalResolution,\r
328 32,\r
329 60\r
330 );\r
331 if (EFI_ERROR (Status)) {\r
332 ReturnStatus = Status;\r
333 }\r
334 }\r
335 }\r
336\r
337 This->Mode->Mode = ModeNumber;\r
338\r
339 Info = This->Mode->Info;\r
340 Info->HorizontalResolution = Mode->HorizontalResolution;\r
341 Info->VerticalResolution = Mode->VerticalResolution;\r
342 Info->PixelsPerScanLine = Mode->HorizontalResolution;\r
343\r
344 //\r
345 // Information is not enough here, so the following items remain unchanged:\r
346 // GraphicsOutputMode->Info->Version, GraphicsOutputMode->Info->PixelFormat\r
347 // GraphicsOutputMode->SizeOfInfo, GraphicsOutputMode->FrameBufferBase, GraphicsOutputMode->FrameBufferSize\r
348 // These items will be initialized/updated when a new GOP device is added into ConsoleSplitter.\r
349 //\r
350\r
351 Private->HardwareNeedsStarting = FALSE;\r
352\r
353 return ReturnStatus;\r
354}\r
355\r
356STATIC\r
357EFI_STATUS\r
358DevNullGraphicsOutputBlt (\r
359 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private,\r
360 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL\r
361 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,\r
362 IN UINTN SourceX,\r
363 IN UINTN SourceY,\r
364 IN UINTN DestinationX,\r
365 IN UINTN DestinationY,\r
366 IN UINTN Width,\r
367 IN UINTN Height,\r
368 IN UINTN Delta OPTIONAL\r
369 )\r
370{\r
371 UINTN SrcY;\r
372 BOOLEAN Forward;\r
373 UINTN Index;\r
374 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltPtr;\r
375 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ScreenPtr;\r
376 UINTN HorizontalResolution;\r
377 UINTN VerticalResolution;\r
378\r
379 if ((BltOperation < EfiBltVideoFill) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) {\r
380 return EFI_INVALID_PARAMETER;\r
381 }\r
382\r
383 if (Width == 0 || Height == 0) {\r
384 return EFI_INVALID_PARAMETER;\r
385 }\r
386\r
387 if (Delta == 0) {\r
388 Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);\r
389 }\r
390\r
391 HorizontalResolution = Private->GraphicsOutput.Mode->Info->HorizontalResolution;\r
392 VerticalResolution = Private->GraphicsOutput.Mode->Info->VerticalResolution;\r
393\r
394 //\r
395 // We need to fill the Virtual Screen buffer with the blt data.\r
396 //\r
397 if (BltOperation == EfiBltVideoToBltBuffer) {\r
398 //\r
399 // Video to BltBuffer: Source is Video, destination is BltBuffer\r
400 //\r
401 if ((SourceY + Height) > VerticalResolution) {\r
402 return EFI_INVALID_PARAMETER;\r
403 }\r
404\r
405 if ((SourceX + Width) > HorizontalResolution) {\r
406 return EFI_INVALID_PARAMETER;\r
407 }\r
408\r
409 BltPtr = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + DestinationY * Delta + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));\r
410 ScreenPtr = &Private->GraphicsOutputBlt[SourceY * HorizontalResolution + SourceX];\r
411 while (Height) {\r
412 CopyMem (BltPtr, ScreenPtr, Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));\r
413 BltPtr = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltPtr + Delta);\r
414 ScreenPtr += HorizontalResolution;\r
415 Height--;\r
416 }\r
417 } else {\r
418 //\r
419 // BltBuffer to Video: Source is BltBuffer, destination is Video\r
420 //\r
421 if (DestinationY + Height > VerticalResolution) {\r
422 return EFI_INVALID_PARAMETER;\r
423 }\r
424\r
425 if (DestinationX + Width > HorizontalResolution) {\r
426 return EFI_INVALID_PARAMETER;\r
427 }\r
428\r
429 if ((BltOperation == EfiBltVideoToVideo) && (DestinationY > SourceY)) {\r
430 //\r
431 // Copy backwards, only care the Video to Video Blt\r
432 //\r
433 ScreenPtr = &Private->GraphicsOutputBlt[(DestinationY + Height - 1) * HorizontalResolution + DestinationX];\r
434 SrcY = SourceY + Height - 1;\r
435 Forward = FALSE;\r
436 } else {\r
437 //\r
438 // Copy forwards, for other cases\r
439 //\r
440 ScreenPtr = &Private->GraphicsOutputBlt[DestinationY * HorizontalResolution + DestinationX];\r
441 SrcY = SourceY;\r
442 Forward = TRUE;\r
443 }\r
444\r
445 while (Height != 0) {\r
446 if (BltOperation == EfiBltVideoFill) {\r
447 for (Index = 0; Index < Width; Index++) {\r
448 ScreenPtr[Index] = *BltBuffer;\r
449 }\r
450 } else {\r
451 if (BltOperation == EfiBltBufferToVideo) {\r
452 BltPtr = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + SrcY * Delta + SourceX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));\r
453 } else {\r
454 BltPtr = &Private->GraphicsOutputBlt[SrcY * HorizontalResolution + SourceX];\r
455 }\r
456\r
457 CopyMem (ScreenPtr, BltPtr, Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));\r
458 }\r
459\r
460 if (Forward) {\r
461 ScreenPtr += HorizontalResolution;\r
462 SrcY ++;\r
463 } else {\r
464 ScreenPtr -= HorizontalResolution;\r
465 SrcY --;\r
466 }\r
467 Height--;\r
468 }\r
469 }\r
470\r
471 return EFI_SUCCESS;\r
472}\r
473\r
474EFI_STATUS\r
475EFIAPI\r
476ConSpliterGraphicsOutputBlt (\r
477 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
478 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL\r
479 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,\r
480 IN UINTN SourceX,\r
481 IN UINTN SourceY,\r
482 IN UINTN DestinationX,\r
483 IN UINTN DestinationY,\r
484 IN UINTN Width,\r
485 IN UINTN Height,\r
486 IN UINTN Delta OPTIONAL\r
487 )\r
488/*++\r
489\r
490 Routine Description:\r
491 The following table defines actions for BltOperations:\r
492 EfiBltVideoFill - Write data from the BltBuffer pixel (SourceX, SourceY)\r
493 directly to every pixel of the video display rectangle\r
494 (DestinationX, DestinationY)\r
495 (DestinationX + Width, DestinationY + Height).\r
496 Only one pixel will be used from the BltBuffer. Delta is NOT used.\r
497 EfiBltVideoToBltBuffer - Read data from the video display rectangle\r
498 (SourceX, SourceY) (SourceX + Width, SourceY + Height) and place it in\r
499 the BltBuffer rectangle (DestinationX, DestinationY )\r
500 (DestinationX + Width, DestinationY + Height). If DestinationX or\r
501 DestinationY is not zero then Delta must be set to the length in bytes\r
502 of a row in the BltBuffer.\r
503 EfiBltBufferToVideo - Write data from the BltBuffer rectangle\r
504 (SourceX, SourceY) (SourceX + Width, SourceY + Height) directly to the\r
505 video display rectangle (DestinationX, DestinationY)\r
506 (DestinationX + Width, DestinationY + Height). If SourceX or SourceY is\r
507 not zero then Delta must be set to the length in bytes of a row in the\r
508 BltBuffer.\r
509 EfiBltVideoToVideo - Copy from the video display rectangle\r
510 (SourceX, SourceY) (SourceX + Width, SourceY + Height) .\r
511 to the video display rectangle (DestinationX, DestinationY)\r
512 (DestinationX + Width, DestinationY + Height).\r
513 The BltBuffer and Delta are not used in this mode.\r
514\r
515 Arguments:\r
516 This - Protocol instance pointer.\r
517 BltBuffer - Buffer containing data to blit into video buffer. This\r
518 buffer has a size of Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)\r
519 BltOperation - Operation to perform on BlitBuffer and video memory\r
520 SourceX - X coordinate of source for the BltBuffer.\r
521 SourceY - Y coordinate of source for the BltBuffer.\r
522 DestinationX - X coordinate of destination for the BltBuffer.\r
523 DestinationY - Y coordinate of destination for the BltBuffer.\r
524 Width - Width of rectangle in BltBuffer in pixels.\r
525 Height - Hight of rectangle in BltBuffer in pixels.\r
526 Delta -\r
527\r
528 Returns:\r
529 EFI_SUCCESS - The Blt operation completed.\r
530 EFI_INVALID_PARAMETER - BltOperation is not valid.\r
531 EFI_DEVICE_ERROR - A hardware error occured writting to the video\r
532 buffer.\r
533\r
534--*/\r
535{\r
536 EFI_STATUS Status;\r
537 TEXT_OUT_SPLITTER_PRIVATE_DATA *Private;\r
538 UINTN Index;\r
539 EFI_STATUS ReturnStatus;\r
540 EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;\r
541 EFI_UGA_DRAW_PROTOCOL *UgaDraw;\r
542\r
543 Private = GRAPHICS_OUTPUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This);\r
544\r
545 //\r
546 // Sync up DevNull GOP device\r
547 //\r
548 ReturnStatus = DevNullGraphicsOutputBlt (\r
549 Private,\r
550 BltBuffer,\r
551 BltOperation,\r
552 SourceX,\r
553 SourceY,\r
554 DestinationX,\r
555 DestinationY,\r
556 Width,\r
557 Height,\r
558 Delta\r
559 );\r
560\r
561 if (Private->ConsoleOutputMode != EfiConsoleControlScreenGraphics) {\r
562 return ReturnStatus;\r
563 }\r
564 //\r
565 // return the worst status met\r
566 //\r
567 for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) {\r
568 GraphicsOutput = Private->TextOutList[Index].GraphicsOutput;\r
569 if (GraphicsOutput != NULL) {\r
570 Status = GraphicsOutput->Blt (\r
571 GraphicsOutput,\r
572 BltBuffer,\r
573 BltOperation,\r
574 SourceX,\r
575 SourceY,\r
576 DestinationX,\r
577 DestinationY,\r
578 Width,\r
579 Height,\r
580 Delta\r
581 );\r
582 if (EFI_ERROR (Status)) {\r
583 ReturnStatus = Status;\r
584 } else if (BltOperation == EfiBltVideoToBltBuffer) {\r
585 //\r
586 // Only need to read the data into buffer one time\r
587 //\r
588 return EFI_SUCCESS;\r
589 }\r
590 }\r
591\r
592 UgaDraw = Private->TextOutList[Index].UgaDraw;\r
593 if (UgaDraw != NULL) {\r
594 Status = UgaDraw->Blt (\r
595 UgaDraw,\r
596 (EFI_UGA_PIXEL *) BltBuffer,\r
597 (EFI_UGA_BLT_OPERATION) BltOperation,\r
598 SourceX,\r
599 SourceY,\r
600 DestinationX,\r
601 DestinationY,\r
602 Width,\r
603 Height,\r
604 Delta\r
605 );\r
606 if (EFI_ERROR (Status)) {\r
607 ReturnStatus = Status;\r
608 } else if (BltOperation == EfiBltVideoToBltBuffer) {\r
609 //\r
610 // Only need to read the data into buffer one time\r
611 //\r
612 return EFI_SUCCESS;\r
613 }\r
614 }\r
615 }\r
616\r
617 return ReturnStatus;\r
618}\r
619\r
620EFI_STATUS\r
621DevNullGopSync (\r
622 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private,\r
623 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,\r
624 IN EFI_UGA_DRAW_PROTOCOL *UgaDraw\r
625 )\r
626{\r
627 if (GraphicsOutput != NULL) {\r
628 return GraphicsOutput->Blt (\r
629 GraphicsOutput,\r
630 Private->GraphicsOutputBlt,\r
631 EfiBltBufferToVideo,\r
632 0,\r
633 0,\r
634 0,\r
635 0,\r
636 Private->GraphicsOutput.Mode->Info->HorizontalResolution,\r
637 Private->GraphicsOutput.Mode->Info->VerticalResolution,\r
638 0\r
639 );\r
640 } else {\r
641 return UgaDraw->Blt (\r
642 UgaDraw,\r
643 (EFI_UGA_PIXEL *) Private->GraphicsOutputBlt,\r
644 EfiUgaBltBufferToVideo,\r
645 0,\r
646 0,\r
647 0,\r
648 0,\r
649 Private->GraphicsOutput.Mode->Info->HorizontalResolution,\r
650 Private->GraphicsOutput.Mode->Info->VerticalResolution,\r
651 0\r
652 );\r
653 }\r
654}\r
655\r
656\r
657EFI_STATUS\r
658DevNullTextOutOutputString (\r
659 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private,\r
660 IN CHAR16 *WString\r
661 )\r
662/*++\r
663\r
664 Routine Description:\r
665 Write a Unicode string to the output device.\r
666\r
667 Arguments:\r
668 Private - Pointer to the console output splitter's private data. It\r
669 indicates the calling context.\r
670 WString - The NULL-terminated Unicode string to be displayed on the output\r
671 device(s). All output devices must also support the Unicode\r
672 drawing defined in this file.\r
673\r
674 Returns:\r
675 EFI_SUCCESS - The string was output to the device.\r
676 EFI_DEVICE_ERROR - The device reported an error while attempting to\r
677 output the text.\r
678 EFI_UNSUPPORTED - The output device's mode is not currently in a\r
679 defined text mode.\r
680 EFI_WARN_UNKNOWN_GLYPH - This warning code indicates that some of the\r
681 characters in the Unicode string could not be\r
682 rendered and were skipped.\r
683\r
684--*/\r
685{\r
686 UINTN SizeScreen;\r
687 UINTN SizeAttribute;\r
688 UINTN Index;\r
689 EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;\r
690 CHAR16 *Screen;\r
691 CHAR16 *NullScreen;\r
692 CHAR16 InsertChar;\r
693 CHAR16 TempChar;\r
694 CHAR16 *PStr;\r
695 INT32 *Attribute;\r
696 INT32 *NullAttributes;\r
697 INT32 CurrentWidth;\r
698 UINTN LastRow;\r
699 UINTN MaxColumn;\r
700\r
701 Mode = &Private->TextOutMode;\r
702 NullScreen = Private->DevNullScreen;\r
703 NullAttributes = Private->DevNullAttributes;\r
704 LastRow = Private->DevNullRows - 1;\r
705 MaxColumn = Private->DevNullColumns;\r
706\r
707 if (Mode->Attribute & EFI_WIDE_ATTRIBUTE) {\r
708 CurrentWidth = 2;\r
709 } else {\r
710 CurrentWidth = 1;\r
711 }\r
712\r
713 while (*WString) {\r
714\r
715 if (*WString == CHAR_BACKSPACE) {\r
716 //\r
717 // If the cursor is at the left edge of the display, then move the cursor\r
718 // one row up.\r
719 //\r
720 if (Mode->CursorColumn == 0 && Mode->CursorRow > 0) {\r
721 Mode->CursorRow--;\r
722 Mode->CursorColumn = (INT32) MaxColumn;\r
723 }\r
724\r
725 //\r
726 // If the cursor is not at the left edge of the display,\r
727 // then move the cursor left one column.\r
728 //\r
729 if (Mode->CursorColumn > 0) {\r
730 Mode->CursorColumn--;\r
731 if (Mode->CursorColumn > 0 &&\r
732 NullAttributes[Mode->CursorRow * MaxColumn + Mode->CursorColumn - 1] & EFI_WIDE_ATTRIBUTE\r
733 ) {\r
734 Mode->CursorColumn--;\r
735\r
736 //\r
737 // Insert an extra backspace\r
738 //\r
739 InsertChar = CHAR_BACKSPACE;\r
740 PStr = WString + 1;\r
741 while (*PStr) {\r
742 TempChar = *PStr;\r
743 *PStr = InsertChar;\r
744 InsertChar = TempChar;\r
745 PStr++;\r
746 }\r
747\r
748 *PStr = InsertChar;\r
749 *(++PStr) = 0;\r
750\r
751 WString++;\r
752 }\r
753 }\r
754\r
755 WString++;\r
756\r
757 } else if (*WString == CHAR_LINEFEED) {\r
758 //\r
759 // If the cursor is at the bottom of the display,\r
760 // then scroll the display one row, and do not update\r
761 // the cursor position. Otherwise, move the cursor down one row.\r
762 //\r
763 if (Mode->CursorRow == (INT32) (LastRow)) {\r
764 //\r
765 // Scroll Screen Up One Row\r
766 //\r
767 SizeAttribute = LastRow * MaxColumn;\r
768 CopyMem (\r
769 NullAttributes,\r
770 NullAttributes + MaxColumn,\r
771 SizeAttribute * sizeof (INT32)\r
772 );\r
773\r
774 //\r
775 // Each row has an ending CHAR_NULL. So one more character each line\r
776 // for DevNullScreen than DevNullAttributes\r
777 //\r
778 SizeScreen = SizeAttribute + LastRow;\r
779 CopyMem (\r
780 NullScreen,\r
781 NullScreen + (MaxColumn + 1),\r
782 SizeScreen * sizeof (CHAR16)\r
783 );\r
784\r
785 //\r
786 // Print Blank Line at last line\r
787 //\r
788 Screen = NullScreen + SizeScreen;\r
789 Attribute = NullAttributes + SizeAttribute;\r
790\r
791 for (Index = 0; Index < MaxColumn; Index++, Screen++, Attribute++) {\r
792 *Screen = ' ';\r
793 *Attribute = Mode->Attribute;\r
794 }\r
795 } else {\r
796 Mode->CursorRow++;\r
797 }\r
798\r
799 WString++;\r
800 } else if (*WString == CHAR_CARRIAGE_RETURN) {\r
801 //\r
802 // Move the cursor to the beginning of the current row.\r
803 //\r
804 Mode->CursorColumn = 0;\r
805 WString++;\r
806 } else {\r
807 //\r
808 // Print the character at the current cursor position and\r
809 // move the cursor right one column. If this moves the cursor\r
810 // past the right edge of the display, then the line should wrap to\r
811 // the beginning of the next line. This is equivalent to inserting\r
812 // a CR and an LF. Note that if the cursor is at the bottom of the\r
813 // display, and the line wraps, then the display will be scrolled\r
814 // one line.\r
815 //\r
816 Index = Mode->CursorRow * MaxColumn + Mode->CursorColumn;\r
817\r
818 while (Mode->CursorColumn < (INT32) MaxColumn) {\r
819 if (*WString == CHAR_NULL) {\r
820 break;\r
821 }\r
822\r
823 if (*WString == CHAR_BACKSPACE) {\r
824 break;\r
825 }\r
826\r
827 if (*WString == CHAR_LINEFEED) {\r
828 break;\r
829 }\r
830\r
831 if (*WString == CHAR_CARRIAGE_RETURN) {\r
832 break;\r
833 }\r
834\r
97a079ed
A
835 if (*WString == UNICODE_WIDE_CHAR || *WString == UNICODE_NARROW_CHAR) {\r
836 CurrentWidth = (*WString == UNICODE_WIDE_CHAR) ? 2 : 1;\r
95276127 837 WString++;\r
838 continue;\r
839 }\r
840\r
841 if (Mode->CursorColumn + CurrentWidth > (INT32) MaxColumn) {\r
842 //\r
843 // If a wide char is at the rightmost column, then move the char\r
844 // to the beginning of the next row\r
845 //\r
846 NullScreen[Index + Mode->CursorRow] = L' ';\r
847 NullAttributes[Index] = Mode->Attribute | (UINT32) EFI_WIDE_ATTRIBUTE;\r
848 Index++;\r
849 Mode->CursorColumn++;\r
850 } else {\r
851 NullScreen[Index + Mode->CursorRow] = *WString;\r
852 NullAttributes[Index] = Mode->Attribute;\r
853 if (CurrentWidth == 1) {\r
854 NullAttributes[Index] &= (~ (UINT32) EFI_WIDE_ATTRIBUTE);\r
855 } else {\r
856 NullAttributes[Index] |= (UINT32) EFI_WIDE_ATTRIBUTE;\r
857 NullAttributes[Index + 1] &= (~ (UINT32) EFI_WIDE_ATTRIBUTE);\r
858 }\r
859\r
860 Index += CurrentWidth;\r
861 WString++;\r
862 Mode->CursorColumn += CurrentWidth;\r
863 }\r
864 }\r
865 //\r
866 // At the end of line, output carriage return and line feed\r
867 //\r
868 if (Mode->CursorColumn >= (INT32) MaxColumn) {\r
869 DevNullTextOutOutputString (Private, mCrLfString);\r
870 }\r
871 }\r
872 }\r
873\r
874 return EFI_SUCCESS;\r
875}\r
876\r
877EFI_STATUS\r
878DevNullTextOutSetMode (\r
879 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private,\r
880 IN UINTN ModeNumber\r
881 )\r
882/*++\r
883\r
884 Routine Description:\r
885 Sets the output device(s) to a specified mode.\r
886\r
887 Arguments:\r
888 Private - Private data structure pointer.\r
889 ModeNumber - The mode number to set.\r
890\r
891 Returns:\r
892 EFI_SUCCESS - The requested text mode was set.\r
893 EFI_DEVICE_ERROR - The device had an error and\r
894 could not complete the request.\r
895 EFI_UNSUPPORTED - The mode number was not valid.\r
896 EFI_OUT_OF_RESOURCES - Out of resources.\r
897\r
898--*/\r
899{\r
900 UINTN Size;\r
901 UINTN Row;\r
902 UINTN Column;\r
903 TEXT_OUT_SPLITTER_QUERY_DATA *Mode;\r
904\r
905 //\r
906 // No extra check for ModeNumber here, as it has been checked in\r
907 // ConSplitterTextOutSetMode. And mode 0 should always be supported.\r
908 //\r
909 Mode = &(Private->TextOutQueryData[ModeNumber]);\r
910 Row = Mode->Rows;\r
911 Column = Mode->Columns;\r
912\r
913 if (Row <= 0 && Column <= 0) {\r
914 return EFI_UNSUPPORTED;\r
915 }\r
916\r
917 if (Private->DevNullColumns != Column || Private->DevNullRows != Row) {\r
918\r
919 Private->TextOutMode.Mode = (INT32) ModeNumber;\r
920 Private->DevNullColumns = Column;\r
921 Private->DevNullRows = Row;\r
922\r
923 if (Private->DevNullScreen != NULL) {\r
924 FreePool (Private->DevNullScreen);\r
925 }\r
926\r
927 Size = (Row * (Column + 1)) * sizeof (CHAR16);\r
928 Private->DevNullScreen = AllocateZeroPool (Size);\r
929 if (Private->DevNullScreen == NULL) {\r
930 return EFI_OUT_OF_RESOURCES;\r
931 }\r
932\r
933 if (Private->DevNullAttributes != NULL) {\r
934 FreePool (Private->DevNullAttributes);\r
935 }\r
936\r
937 Size = Row * Column * sizeof (INT32);\r
938 Private->DevNullAttributes = AllocateZeroPool (Size);\r
939 if (Private->DevNullAttributes == NULL) {\r
940 return EFI_OUT_OF_RESOURCES;\r
941 }\r
942 }\r
943\r
944 DevNullTextOutClearScreen (Private);\r
945\r
946 return EFI_SUCCESS;\r
947}\r
948\r
949EFI_STATUS\r
950DevNullTextOutClearScreen (\r
951 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private\r
952 )\r
953/*++\r
954\r
955 Routine Description:\r
956 Clears the output device(s) display to the currently selected background\r
957 color.\r
958\r
959 Arguments:\r
960 Private - Protocol instance pointer.\r
961\r
962 Returns:\r
963 EFI_SUCCESS - The operation completed successfully.\r
964 EFI_DEVICE_ERROR - The device had an error and\r
965 could not complete the request.\r
966 EFI_UNSUPPORTED - The output device is not in a valid text mode.\r
967\r
968--*/\r
969{\r
970 UINTN Row;\r
971 UINTN Column;\r
972 CHAR16 *Screen;\r
973 INT32 *Attributes;\r
974 INT32 CurrentAttribute;\r
975\r
976 //\r
977 // Clear the DevNull Text Out Buffers.\r
978 // The screen is filled with spaces.\r
979 // The attributes are all synced with the current Simple Text Out Attribute\r
980 //\r
981 Screen = Private->DevNullScreen;\r
982 Attributes = Private->DevNullAttributes;\r
983 CurrentAttribute = Private->TextOutMode.Attribute;\r
984\r
985 for (Row = 0; Row < Private->DevNullRows; Row++) {\r
986 for (Column = 0; Column < Private->DevNullColumns; Column++, Screen++, Attributes++) {\r
987 *Screen = ' ';\r
988 *Attributes = CurrentAttribute;\r
989 }\r
990 //\r
991 // Each line of the screen has a NULL on the end so we must skip over it\r
992 //\r
993 Screen++;\r
994 }\r
995\r
996 DevNullTextOutSetCursorPosition (Private, 0, 0);\r
997\r
998 return DevNullTextOutEnableCursor (Private, TRUE);\r
999}\r
1000\r
1001EFI_STATUS\r
1002DevNullTextOutSetCursorPosition (\r
1003 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private,\r
1004 IN UINTN Column,\r
1005 IN UINTN Row\r
1006 )\r
1007/*++\r
1008\r
1009 Routine Description:\r
1010 Sets the current coordinates of the cursor position\r
1011\r
1012 Arguments:\r
1013 Private - Protocol instance pointer.\r
1014 Column, Row - the position to set the cursor to. Must be greater than or\r
1015 equal to zero and less than the number of columns and rows\r
1016 by QueryMode ().\r
1017\r
1018 Returns:\r
1019 EFI_SUCCESS - The operation completed successfully.\r
1020 EFI_DEVICE_ERROR - The device had an error and\r
1021 could not complete the request.\r
1022 EFI_UNSUPPORTED - The output device is not in a valid text mode, or the\r
1023 cursor position is invalid for the current mode.\r
1024\r
1025--*/\r
1026{\r
1027 //\r
1028 // No need to do extra check here as whether (Column, Row) is valid has\r
1029 // been checked in ConSplitterTextOutSetCursorPosition. And (0, 0) should\r
1030 // always be supported.\r
1031 //\r
1032 Private->TextOutMode.CursorColumn = (INT32) Column;\r
1033 Private->TextOutMode.CursorRow = (INT32) Row;\r
1034\r
1035 return EFI_SUCCESS;\r
1036}\r
1037\r
1038EFI_STATUS\r
1039DevNullTextOutEnableCursor (\r
1040 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private,\r
1041 IN BOOLEAN Visible\r
1042 )\r
1043/*++\r
1044 Routine Description:\r
1045\r
1046 Implements SIMPLE_TEXT_OUTPUT.EnableCursor().\r
1047 In this driver, the cursor cannot be hidden.\r
1048\r
1049 Arguments:\r
1050\r
1051 Private - Indicates the calling context.\r
1052\r
1053 Visible - If TRUE, the cursor is set to be visible, If FALSE, the cursor\r
1054 is set to be invisible.\r
1055\r
1056 Returns:\r
1057\r
1058 EFI_SUCCESS - The request is valid.\r
1059\r
1060\r
1061--*/\r
1062{\r
1063 Private->TextOutMode.CursorVisible = Visible;\r
1064\r
1065 return EFI_SUCCESS;\r
1066}\r
1067\r
1068EFI_STATUS\r
1069DevNullSyncGopStdOut (\r
1070 IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private\r
1071 )\r
1072/*++\r
1073 Routine Description:\r
1074 Take the DevNull TextOut device and update the Simple Text Out on every\r
1075 UGA device.\r
1076\r
1077 Arguments:\r
1078 Private - Indicates the calling context.\r
1079\r
1080 Returns:\r
1081 EFI_SUCCESS - The request is valid.\r
1082 other - Return status of TextOut->OutputString ()\r
1083\r
1084--*/\r
1085{\r
1086 EFI_STATUS Status;\r
1087 EFI_STATUS ReturnStatus;\r
1088 UINTN Row;\r
1089 UINTN Column;\r
1090 UINTN List;\r
1091 UINTN MaxColumn;\r
1092 UINTN CurrentColumn;\r
1093 UINTN StartRow;\r
1094 UINTN StartColumn;\r
1095 INT32 StartAttribute;\r
1096 BOOLEAN StartCursorState;\r
1097 CHAR16 *Screen;\r
1098 CHAR16 *Str;\r
1099 CHAR16 *Buffer;\r
1100 CHAR16 *BufferTail;\r
1101 CHAR16 *ScreenStart;\r
1102 INT32 CurrentAttribute;\r
1103 INT32 *Attributes;\r
1104 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Sto;\r
1105\r
1106 //\r
1107 // Save the devices Attributes, Cursor enable state and location\r
1108 //\r
1109 StartColumn = Private->TextOutMode.CursorColumn;\r
1110 StartRow = Private->TextOutMode.CursorRow;\r
1111 StartAttribute = Private->TextOutMode.Attribute;\r
1112 StartCursorState = Private->TextOutMode.CursorVisible;\r
1113\r
1114 for (List = 0; List < Private->CurrentNumberOfConsoles; List++) {\r
1115\r
1116 Sto = Private->TextOutList[List].TextOut;\r
1117\r
1118 //\r
1119 // Skip non GOP/UGA devices\r
1120 //\r
1121 if ((Private->TextOutList[List].GraphicsOutput != NULL) || (Private->TextOutList[List].UgaDraw != NULL)) {\r
1122 Sto->EnableCursor (Sto, FALSE);\r
1123 Sto->ClearScreen (Sto);\r
1124 }\r
1125 }\r
1126\r
1127 ReturnStatus = EFI_SUCCESS;\r
1128 Screen = Private->DevNullScreen;\r
1129 Attributes = Private->DevNullAttributes;\r
1130 MaxColumn = Private->DevNullColumns;\r
1131\r
1132 Buffer = AllocateZeroPool ((MaxColumn + 1) * sizeof (CHAR16));\r
1133\r
1134 for (Row = 0; Row < Private->DevNullRows; Row++, Screen += (MaxColumn + 1), Attributes += MaxColumn) {\r
1135\r
1136 if (Row == (Private->DevNullRows - 1)) {\r
1137 //\r
1138 // Don't ever sync the last character as it will scroll the screen\r
1139 //\r
1140 Screen[MaxColumn - 1] = 0x00;\r
1141 }\r
1142\r
1143 Column = 0;\r
1144 while (Column < MaxColumn) {\r
1145 if (Screen[Column]) {\r
1146 CurrentAttribute = Attributes[Column];\r
1147 CurrentColumn = Column;\r
1148 ScreenStart = &Screen[Column];\r
1149\r
1150 //\r
1151 // the line end is alway 0x0. So Column should be less than MaxColumn\r
1152 // It should be still in the same row\r
1153 //\r
1154 for (Str = ScreenStart, BufferTail = Buffer; *Str != 0; Str++, Column++) {\r
1155\r
1156 if (Attributes[Column] != CurrentAttribute) {\r
1157 Column--;\r
1158 break;\r
1159 }\r
1160\r
1161 *BufferTail = *Str;\r
1162 BufferTail++;\r
1163 if (Attributes[Column] & EFI_WIDE_ATTRIBUTE) {\r
1164 Str++;\r
1165 Column++;\r
1166 }\r
1167 }\r
1168\r
1169 *BufferTail = 0;\r
1170\r
1171 for (List = 0; List < Private->CurrentNumberOfConsoles; List++) {\r
1172\r
1173 Sto = Private->TextOutList[List].TextOut;\r
1174\r
1175 //\r
1176 // Skip non GOP/UGA devices\r
1177 //\r
1178 if ((Private->TextOutList[List].GraphicsOutput != NULL) || (Private->TextOutList[List].UgaDraw != NULL)) {\r
1179 Sto->SetAttribute (Sto, CurrentAttribute);\r
1180 Sto->SetCursorPosition (Sto, CurrentColumn, Row);\r
1181 Status = Sto->OutputString (Sto, Buffer);\r
1182 if (EFI_ERROR (Status)) {\r
1183 ReturnStatus = Status;\r
1184 }\r
1185 }\r
1186 }\r
1187\r
1188 }\r
1189\r
1190 Column++;\r
1191 }\r
1192 }\r
1193 //\r
1194 // Restore the devices Attributes, Cursor enable state and location\r
1195 //\r
1196 for (List = 0; List < Private->CurrentNumberOfConsoles; List++) {\r
1197 Sto = Private->TextOutList[List].TextOut;\r
1198\r
1199 //\r
1200 // Skip non GOP/UGA devices\r
1201 //\r
1202 if ((Private->TextOutList[List].GraphicsOutput != NULL) || (Private->TextOutList[List].UgaDraw != NULL)) {\r
1203 Sto->SetAttribute (Sto, StartAttribute);\r
1204 Sto->SetCursorPosition (Sto, StartColumn, StartRow);\r
1205 Status = Sto->EnableCursor (Sto, StartCursorState);\r
1206 if (EFI_ERROR (Status)) {\r
1207 ReturnStatus = Status;\r
1208 }\r
1209 }\r
1210 }\r
1211\r
1212 FreePool (Buffer);\r
1213\r
1214 return ReturnStatus;\r
1215}\r