]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DisplayUpdateProgressLibGraphics/DisplayUpdateProgressLibGraphics.c
MdeModulePkg: Add DisplayUpdateProgressLib instances
[mirror_edk2.git] / MdeModulePkg / Library / DisplayUpdateProgressLibGraphics / DisplayUpdateProgressLibGraphics.c
1 /** @file
2 Provides services to display completion progress of a firmware update on a
3 graphical console that supports the Graphics Output Protocol.
4
5 Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>
6 Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 1. Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
20 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
24 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 **/
28
29 #include <PiDxe.h>
30 #include <Library/BaseMemoryLib.h>
31 #include <Library/MemoryAllocationLib.h>
32 #include <Library/UefiBootServicesTableLib.h>
33 #include <Library/DebugLib.h>
34 #include <Library/BaseLib.h>
35 #include <Library/UefiLib.h>
36
37 #include <Protocol/GraphicsOutput.h>
38 #include <Protocol/BootLogo2.h>
39
40 //
41 // Values in percent of of logo height.
42 //
43 #define LOGO_BOTTOM_PADDING 20
44 #define PROGRESS_BLOCK_HEIGHT 10
45
46 //
47 // Graphics Output Protocol instance to display progress bar
48 //
49 EFI_GRAPHICS_OUTPUT_PROTOCOL *mGop = NULL;
50
51 //
52 // Set to 100 percent so it is reset on first call.
53 //
54 UINTN mPreviousProgress = 100;
55
56 //
57 // Display coordinates for the progress bar.
58 //
59 UINTN mStartX = 0;
60 UINTN mStartY = 0;
61
62 //
63 // Width and height of the progress bar.
64 //
65 UINTN mBlockWidth = 0;
66 UINTN mBlockHeight = 0;
67
68 //
69 // GOP bitmap of the progress bar. Initialized on every new progress of 100%
70 //
71 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mBlockBitmap;
72
73 //
74 // GOP bitmap of the progress bar backround. Initialized once.
75 //
76 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mProgressBarBackground;
77
78 //
79 // Default mask used to detect the left, right , top, and bottom of logo. Only
80 // green and blue pixels are used for logo detection.
81 //
82 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mLogoDetectionColorMask = {
83 {
84 0xFF, // Blue
85 0xFF, // Green
86 0x00, // Red
87 0x00 // Reserved
88 }
89 };
90
91 //
92 // Background color of progress bar. Grey.
93 //
94 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mProgressBarBackgroundColor = {
95 {
96 0x80, // Blue
97 0x80, // Green
98 0x80, // Red
99 0x00 // Reserved
100 }
101 };
102
103 //
104 // Default color of progress completion. White.
105 //
106 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mProgressBarDefaultColor = {
107 {
108 0xFF, // Blue
109 0xFF, // Green
110 0xFF, // Red
111 0x00 // Reserved
112 }
113 };
114
115 //
116 // Set to TRUE if a valid Graphics Output Protocol is found and the progress
117 // bar fits under the boot logo using the current graphics mode.
118 //
119 BOOLEAN mGraphicsGood = FALSE;
120
121 /*
122 Internal function used to find the bounds of the white logo (on black or
123 red background).
124
125 These bounds are then computed to find the block size, 0%, 100%, etc.
126
127 */
128 VOID
129 FindDim (
130 VOID
131 )
132 {
133 EFI_STATUS Status;
134 INTN LogoX;
135 INTN LogoStartX;
136 INTN LogoEndX;
137 INTN LogoY;
138 INTN LogoStartY;
139 INTN LogoEndY;
140 UINTN OffsetX; // Logo screen coordinate
141 UINTN OffsetY; // Logo screen coordinate
142 UINTN Width; // Width of logo in pixels
143 UINTN Height; // Height of logo in pixels
144 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Logo;
145 EDKII_BOOT_LOGO2_PROTOCOL *BootLogo;
146 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *Pixel;
147
148 Logo = NULL;
149 BootLogo = NULL;
150
151 //
152 // Return if a Graphics Output Protocol ha snot been found.
153 //
154 if (mGop == NULL) {
155 DEBUG ((DEBUG_ERROR, "No GOP found. No progress bar support. \n"));
156 return;
157 }
158
159 //
160 // Get boot logo protocol so we know where on the screen to grab
161 //
162 Status = gBS->LocateProtocol (
163 &gEdkiiBootLogo2ProtocolGuid,
164 NULL,
165 (VOID **)&BootLogo
166 );
167 if ((BootLogo == NULL) || (EFI_ERROR (Status))) {
168 DEBUG ((DEBUG_ERROR, "Failed to locate gEdkiiBootLogo2ProtocolGuid. No Progress bar support. \n", Status));
169 return;
170 }
171
172 //
173 // Get logo location and size
174 //
175 Status = BootLogo->GetBootLogo (
176 BootLogo,
177 &Logo,
178 &OffsetX,
179 &OffsetY,
180 &Width,
181 &Height
182 );
183 if (EFI_ERROR (Status)) {
184 DEBUG ((DEBUG_ERROR, "Failed to Get Boot Logo Status = %r. No Progress bar support. \n", Status));
185 return;
186 }
187
188 //
189 // Within logo buffer find where the actual logo starts/ends
190 //
191 LogoEndX = 0;
192 LogoEndY = 0;
193
194 //
195 // Find left side of logo in logo coordinates
196 //
197 for (LogoX = 0, LogoStartX = Width; LogoX < LogoStartX; LogoX++) {
198 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + LogoX);
199 for (LogoY = 0; LogoY < (INTN)Height; LogoY++) {
200 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
201 LogoStartX = LogoX;
202 //
203 // For loop searches from right side back to this column.
204 //
205 LogoEndX = LogoX;
206 DEBUG ((DEBUG_INFO, "StartX found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
207 break;
208 }
209 Pixel = Pixel + Width;
210 }
211 }
212
213 //
214 // Find right side of logo
215 //
216 for (LogoX = Width - 1; LogoX >= LogoEndX; LogoX--) {
217 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + LogoX);
218 for (LogoY = 0; LogoY < (INTN)Height; LogoY++) {
219 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
220 LogoEndX = LogoX;
221 DEBUG ((DEBUG_INFO, "EndX found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
222 break;
223 }
224 Pixel = Pixel + Width;
225 }
226 }
227
228 //
229 // Compute mBlockWidth
230 //
231 mBlockWidth = ((LogoEndX - LogoStartX) + 99) / 100;
232
233 //
234 // Adjust mStartX based on block width so it is centered under logo
235 //
236 mStartX = LogoStartX + OffsetX - (((mBlockWidth * 100) - (LogoEndX - LogoStartX)) / 2);
237 DEBUG ((DEBUG_INFO, "mBlockWidth set to 0x%X\n", mBlockWidth));
238 DEBUG ((DEBUG_INFO, "mStartX set to 0x%X\n", mStartX));
239
240 //
241 // Find the top of the logo
242 //
243 for (LogoY = 0, LogoStartY = Height; LogoY < LogoStartY; LogoY++) {
244 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + (Width * LogoY));
245 for (LogoX = 0; LogoX < (INTN)Width; LogoX++) {
246 //not black or red
247 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
248 LogoStartY = LogoY;
249 LogoEndY = LogoY; //for next loop will search from bottom side back to this row.
250 DEBUG ((DEBUG_INFO, "StartY found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
251 break;
252 }
253 Pixel++;
254 }
255 }
256
257 //
258 // Find the bottom of the logo
259 //
260 for (LogoY = Height - 1; LogoY >= LogoEndY; LogoY--) {
261 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + (Width * LogoY));
262 for (LogoX = 0; LogoX < (INTN)Width; LogoX++) {
263 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
264 LogoEndY = LogoY;
265 DEBUG ((DEBUG_INFO, "EndY found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
266 break;
267 }
268 Pixel++;
269 }
270 }
271
272 //
273 // Compute bottom padding (distance between logo bottom and progress bar)
274 //
275 mStartY = (((LogoEndY - LogoStartY) * LOGO_BOTTOM_PADDING) / 100) + LogoEndY + OffsetY;
276
277 //
278 // Compute progress bar height
279 //
280 mBlockHeight = (((LogoEndY - LogoStartY) * PROGRESS_BLOCK_HEIGHT) / 100);
281
282 DEBUG ((DEBUG_INFO, "mBlockHeight set to 0x%X\n", mBlockHeight));
283
284 //
285 // Create progress bar background (one time init).
286 //
287 mProgressBarBackground = AllocatePool (mBlockWidth * 100 * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
288 if (mProgressBarBackground == NULL) {
289 DEBUG ((DEBUG_ERROR, "Failed to allocate progress bar background\n"));
290 return;
291 }
292
293 //
294 // Fill the progress bar with the background color
295 //
296 SetMem32 (
297 mProgressBarBackground,
298 (mBlockWidth * 100 * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
299 mProgressBarBackgroundColor.Raw
300 );
301
302 //
303 // Allocate mBlockBitmap
304 //
305 mBlockBitmap = AllocatePool (mBlockWidth * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
306 if (mBlockBitmap == NULL) {
307 FreePool (mProgressBarBackground);
308 DEBUG ((DEBUG_ERROR, "Failed to allocate block\n"));
309 return;
310 }
311
312 //
313 // Check screen width and height and make sure it fits.
314 //
315 if ((mBlockHeight > Height) || (mBlockWidth > Width) || (mBlockHeight < 1) || (mBlockWidth < 1)) {
316 DEBUG ((DEBUG_ERROR, "DisplayUpdateProgressLib - Progress - Failed to get valid width and height.\n"));
317 DEBUG ((DEBUG_ERROR, "DisplayUpdateProgressLib - Progress - mBlockHeight: 0x%X mBlockWidth: 0x%X.\n", mBlockHeight, mBlockWidth));
318 FreePool (mProgressBarBackground);
319 FreePool (mBlockBitmap);
320 return;
321 }
322
323 mGraphicsGood = TRUE;
324 }
325
326 /**
327 Function indicates the current completion progress of a firmware update.
328 Platform may override with its own specific function.
329
330 @param[in] Completion A value between 0 and 100 indicating the current
331 completion progress of a firmware update. This
332 value must the the same or higher than previous
333 calls to this service. The first call of 0 or a
334 value of 0 after reaching a value of 100 resets
335 the progress indicator to 0.
336 @param[in] Color Color of the progress indicator. Only used when
337 Completion is 0 to set the color of the progress
338 indicator. If Color is NULL, then the default color
339 is used.
340
341 @retval EFI_SUCCESS Progress displayed successfully.
342 @retval EFI_INVALID_PARAMETER Completion is not in range 0..100.
343 @retval EFI_INVALID_PARAMETER Completion is less than Completion value from
344 a previous call to this service.
345 @retval EFI_NOT_READY The device used to indicate progress is not
346 available.
347 **/
348 EFI_STATUS
349 EFIAPI
350 DisplayUpdateProgress (
351 IN UINTN Completion,
352 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *Color OPTIONAL
353 )
354 {
355 EFI_STATUS Status;
356 UINTN PreX;
357 UINTN Index;
358
359 //
360 // Check range
361 //
362 if (Completion > 100) {
363 return EFI_INVALID_PARAMETER;
364 }
365
366 //
367 // Check to see if this Completion percentage has already been displayed
368 //
369 if (Completion == mPreviousProgress) {
370 return EFI_SUCCESS;
371 }
372
373 //
374 // Find Graphics Output Protocol if not already set. 1 time.
375 //
376 if (mGop == NULL) {
377 Status = gBS->HandleProtocol (
378 gST->ConsoleOutHandle,
379 &gEfiGraphicsOutputProtocolGuid,
380 (VOID**)&mGop
381 );
382 if (EFI_ERROR (Status)) {
383 Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&mGop);
384 if (EFI_ERROR (Status)) {
385 mGop = NULL;
386 DEBUG ((DEBUG_ERROR, "Show Progress Function could not locate GOP. Status = %r\n", Status));
387 return EFI_NOT_READY;
388 }
389 }
390
391 //
392 // Run once
393 //
394 FindDim ();
395 }
396
397 //
398 // Make sure a valid start, end, and size info are available (find the Logo)
399 //
400 if (!mGraphicsGood) {
401 DEBUG ((DEBUG_INFO, "Graphics Not Good. Not doing any onscreen visual display\n"));
402 return EFI_NOT_READY;
403 }
404
405 //
406 // Do special init on first call of each progress session
407 //
408 if (mPreviousProgress == 100) {
409 //
410 // Draw progress bar background
411 //
412 mGop->Blt (
413 mGop,
414 mProgressBarBackground,
415 EfiBltBufferToVideo,
416 0,
417 0,
418 mStartX,
419 mStartY,
420 (mBlockWidth * 100),
421 mBlockHeight,
422 0
423 );
424
425 DEBUG ((DEBUG_VERBOSE, "Color is 0x%X\n",
426 (Color == NULL) ? mProgressBarDefaultColor.Raw : Color->Raw
427 ));
428
429 //
430 // Update block bitmap with correct color
431 //
432 SetMem32 (
433 mBlockBitmap,
434 (mBlockWidth * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
435 (Color == NULL) ? mProgressBarDefaultColor.Raw : Color->Raw
436 );
437
438 //
439 // Clear previous
440 //
441 mPreviousProgress = 0;
442 }
443
444 //
445 // Can not update progress bar if Completion is less than previous
446 //
447 if (Completion < mPreviousProgress) {
448 DEBUG ((DEBUG_WARN, "WARNING: Completion (%d) should not be lesss than Previous (%d)!!!\n", Completion, mPreviousProgress));
449 return EFI_INVALID_PARAMETER;
450 }
451
452 PreX = ((mPreviousProgress * mBlockWidth) + mStartX);
453 for (Index = 0; Index < (Completion - mPreviousProgress); Index++) {
454 //
455 // Show progress by coloring new area
456 //
457 mGop->Blt (
458 mGop,
459 mBlockBitmap,
460 EfiBltBufferToVideo,
461 0,
462 0,
463 PreX,
464 mStartY,
465 mBlockWidth,
466 mBlockHeight,
467 0
468 );
469 PreX += mBlockWidth;
470 }
471
472 mPreviousProgress = Completion;
473
474 return EFI_SUCCESS;
475 }