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