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