]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Library/DisplayUpdateProgressLibGraphics/DisplayUpdateProgressLibGraphics.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Library / DisplayUpdateProgressLibGraphics / DisplayUpdateProgressLibGraphics.c
... / ...
CommitLineData
1/** @file\r
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
8 SPDX-License-Identifier: BSD-2-Clause-Patent\r
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
104/**\r
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
110**/\r
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