]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c
SecurityPkg OpalPasswordSupportLib: Add comments for the used protocol in inf file.
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmDriverHealth.c
CommitLineData
067ed98a
RN
1/** @file\r
2 Library functions which relates with driver health.\r
3\r
4982ea57 4(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>\r
067ed98a
RN
5Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "InternalBm.h"\r
17\r
18GLOBAL_REMOVE_IF_UNREFERENCED\r
19 CHAR16 *mBmHealthStatusText[] = {\r
20 L"Healthy",\r
21 L"Repair Required",\r
22 L"Configuration Required",\r
23 L"Failed",\r
24 L"Reconnect Required",\r
25 L"Reboot Required"\r
26 };\r
27\r
28/**\r
29 Return the controller name.\r
30\r
31 @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.\r
32 @param ControllerHandle The handle of a controller that the driver specified by DriverBindingHandle is managing.\r
33 This handle specifies the controller whose name is to be returned.\r
34 @param ChildHandle The handle of the child controller to retrieve the name of. This is an\r
35 optional parameter that may be NULL. It will be NULL for device drivers.\r
36 It will also be NULL for bus drivers that attempt to retrieve the name\r
37 of the bus controller. It will not be NULL for a bus driver that attempts\r
38 to retrieve the name of a child controller.\r
39\r
40 @return A pointer to the Unicode string to return. This Unicode string is the name of the controller\r
41 specified by ControllerHandle and ChildHandle.\r
42**/\r
43CHAR16 *\r
44BmGetControllerName (\r
45 IN EFI_HANDLE DriverHealthHandle,\r
46 IN EFI_HANDLE ControllerHandle,\r
47 IN EFI_HANDLE ChildHandle\r
48 )\r
49{\r
50 EFI_STATUS Status;\r
51 CHAR16 *ControllerName;\r
52 CHAR8 *LanguageVariable;\r
53 CHAR8 *BestLanguage;\r
54 BOOLEAN Iso639Language;\r
55 EFI_COMPONENT_NAME_PROTOCOL *ComponentName;\r
56\r
57 ControllerName = NULL;\r
58\r
59 //\r
60 // Locate Component Name (2) protocol on the driver binging handle.\r
61 //\r
62 Iso639Language = FALSE;\r
63 Status = gBS->HandleProtocol (\r
64 DriverHealthHandle,\r
65 &gEfiComponentName2ProtocolGuid,\r
66 (VOID **) &ComponentName\r
67 );\r
68 if (EFI_ERROR (Status)) {\r
69 Status = gBS->HandleProtocol (\r
70 DriverHealthHandle,\r
71 &gEfiComponentNameProtocolGuid,\r
72 (VOID **) &ComponentName\r
73 );\r
74 if (!EFI_ERROR (Status)) {\r
75 Iso639Language = TRUE;\r
76 }\r
77 }\r
78\r
79 if (!EFI_ERROR (Status)) {\r
cf34f86b 80 GetEfiGlobalVariable2 (Iso639Language ? L"Lang" : L"PlatformLang", (VOID**)&LanguageVariable, NULL);\r
067ed98a
RN
81 BestLanguage = GetBestLanguage(\r
82 ComponentName->SupportedLanguages,\r
83 Iso639Language,\r
84 (LanguageVariable != NULL) ? LanguageVariable : "",\r
85 Iso639Language ? "eng" : "en-US",\r
86 NULL\r
87 );\r
88 if (LanguageVariable != NULL) {\r
89 FreePool (LanguageVariable);\r
90 }\r
91\r
92 Status = ComponentName->GetControllerName (\r
93 ComponentName,\r
94 ControllerHandle, \r
95 ChildHandle,\r
96 BestLanguage,\r
97 &ControllerName\r
98 );\r
99 }\r
100\r
101 if (!EFI_ERROR (Status)) {\r
102 return AllocateCopyPool (StrSize (ControllerName), ControllerName);\r
103 } else {\r
104 return ConvertDevicePathToText (\r
105 DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle),\r
106 FALSE,\r
107 FALSE\r
108 );\r
109 }\r
110}\r
111\r
112/**\r
113 Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol\r
114\r
115 @param DriverHealthInfo Pointer to the Driver Health information entry.\r
116**/\r
117VOID\r
118BmDisplayMessages (\r
119 IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo\r
120 )\r
121{\r
122 UINTN Index;\r
123 EFI_STRING String;\r
124 CHAR16 *ControllerName;\r
125\r
126 if (DriverHealthInfo->MessageList == NULL ||\r
127 DriverHealthInfo->MessageList[0].HiiHandle == NULL) {\r
128 return;\r
129 }\r
130\r
131 ControllerName = BmGetControllerName (\r
132 DriverHealthInfo->DriverHealthHandle,\r
133 DriverHealthInfo->ControllerHandle, \r
134 DriverHealthInfo->ChildHandle\r
135 );\r
136\r
137 DEBUG ((EFI_D_INFO, "Controller: %s\n", ControllerName));\r
138 Print (L"Controller: %s\n", ControllerName);\r
139 for (Index = 0; DriverHealthInfo->MessageList[Index].HiiHandle != NULL; Index++) {\r
140 String = HiiGetString (\r
141 DriverHealthInfo->MessageList[Index].HiiHandle,\r
142 DriverHealthInfo->MessageList[Index].StringId,\r
143 NULL\r
144 );\r
145 if (String != NULL) {\r
146 Print (L" %s\n", String);\r
147 DEBUG ((EFI_D_INFO, " %s\n", String));\r
148 FreePool (String);\r
149 }\r
150 }\r
151\r
152 if (ControllerName != NULL) {\r
153 FreePool (ControllerName);\r
154 }\r
155}\r
156\r
157/**\r
158 The repair notify function.\r
159 @param Value A value between 0 and Limit that identifies the current progress\r
160 of the repair operation.\r
161 @param Limit The maximum value of Value for the current repair operation.\r
162 If Limit is 0, then the completion progress is indeterminate.\r
163 For example, a driver that wants to specify progress in percent\r
164 would use a Limit value of 100.\r
165\r
166 @retval EFI_SUCCESS Successfully return from the notify function.\r
167**/\r
168EFI_STATUS\r
169EFIAPI\r
170BmRepairNotify (\r
171 IN UINTN Value,\r
172 IN UINTN Limit\r
173 )\r
174{\r
175 DEBUG ((EFI_D_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit));\r
176 Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit);\r
177\r
178 return EFI_SUCCESS;\r
179}\r
180\r
181/**\r
182 Collect the Driver Health status of a single controller.\r
183 \r
184 @param DriverHealthInfo A pointer to the array containing all of the platform driver health information.\r
185 @param Count Return the updated array count.\r
186 @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.\r
187 @param ControllerHandle The handle of the controller..\r
188 @param ChildHandle The handle of the child controller to retrieve the health\r
189 status on. This is an optional parameter that may be NULL.\r
190\r
191 @retval Status The status returned from GetHealthStatus.\r
192 @retval EFI_ABORTED The health status is healthy so no further query is needed.\r
193\r
194**/\r
195EFI_STATUS\r
196BmGetSingleControllerHealthStatus (\r
197 IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo,\r
198 IN OUT UINTN *Count,\r
199 IN EFI_HANDLE DriverHealthHandle,\r
200 IN EFI_HANDLE ControllerHandle, OPTIONAL\r
201 IN EFI_HANDLE ChildHandle OPTIONAL\r
202 )\r
203{\r
204 EFI_STATUS Status;\r
205 EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth;\r
206 EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList;\r
207 EFI_HII_HANDLE FormHiiHandle;\r
208 EFI_DRIVER_HEALTH_STATUS HealthStatus;\r
209\r
210 ASSERT (DriverHealthHandle != NULL);\r
211 //\r
212 // Retrieve the Driver Health Protocol from DriverHandle\r
213 //\r
214 Status = gBS->HandleProtocol (\r
215 DriverHealthHandle,\r
216 &gEfiDriverHealthProtocolGuid,\r
217 (VOID **) &DriverHealth\r
218 );\r
219 ASSERT_EFI_ERROR (Status);\r
220 \r
221\r
222 if (ControllerHandle == NULL) {\r
223 //\r
224 // If ControllerHandle is NULL, the return the cumulative health status of the driver\r
225 //\r
226 Status = DriverHealth->GetHealthStatus (DriverHealth, NULL, NULL, &HealthStatus, NULL, NULL);\r
227 if (!EFI_ERROR (Status) && HealthStatus == EfiDriverHealthStatusHealthy) {\r
228 *DriverHealthInfo = ReallocatePool (\r
229 (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),\r
230 (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),\r
231 *DriverHealthInfo\r
232 );\r
233 ASSERT (*DriverHealthInfo != NULL);\r
234\r
235 (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;\r
236 (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;\r
237 (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;\r
238\r
239 *Count = *Count + 1;\r
240\r
241 Status = EFI_ABORTED;\r
242 }\r
243 return Status;\r
244 }\r
245\r
246 MessageList = NULL;\r
247 FormHiiHandle = NULL;\r
248\r
249 //\r
250 // Collect the health status with the optional HII message list\r
251 //\r
252 Status = DriverHealth->GetHealthStatus (DriverHealth, ControllerHandle, ChildHandle, &HealthStatus, &MessageList, &FormHiiHandle);\r
253 if (!EFI_ERROR (Status)) {\r
254 *DriverHealthInfo = ReallocatePool (\r
255 (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),\r
256 (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),\r
257 *DriverHealthInfo\r
258 );\r
259 ASSERT (*DriverHealthInfo != NULL);\r
260 (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;\r
261 (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;\r
262 (*DriverHealthInfo)[*Count].ControllerHandle = ControllerHandle;\r
263 (*DriverHealthInfo)[*Count].ChildHandle = ChildHandle;\r
264 (*DriverHealthInfo)[*Count].HiiHandle = FormHiiHandle;\r
265 (*DriverHealthInfo)[*Count].MessageList = MessageList;\r
266 (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;\r
267\r
268 *Count = *Count + 1;\r
269 }\r
270\r
271 return Status;\r
272}\r
273\r
274/**\r
275 Return all the Driver Health information.\r
276\r
277 When the cumulative health status of all the controllers managed by the\r
278 driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one\r
279 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such\r
280 EFI_DRIVER_HEALTH_PROTOCOL instance.\r
281 Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO\r
282 entry. Additionally every child controller creates one\r
283 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver.\r
284\r
285 @param Count Return the count of the Driver Health information.\r
286\r
287 @retval NULL No Driver Health information is returned.\r
288 @retval !NULL Pointer to the Driver Health information array.\r
289**/\r
290EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *\r
291EFIAPI\r
292EfiBootManagerGetDriverHealthInfo (\r
293 UINTN *Count\r
294 )\r
295{\r
296 EFI_STATUS Status;\r
297 UINTN NumHandles;\r
298 EFI_HANDLE *DriverHealthHandles;\r
067ed98a
RN
299 UINTN DriverHealthIndex;\r
300 EFI_HANDLE *Handles;\r
301 UINTN HandleCount;\r
302 UINTN ControllerIndex;\r
303 UINTN ChildIndex;\r
304 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;\r
305\r
306 //\r
307 // Initialize local variables\r
308 //\r
309 *Count = 0;\r
310 DriverHealthInfo = NULL;\r
311 Handles = NULL;\r
312 DriverHealthHandles = NULL;\r
313 NumHandles = 0;\r
314 HandleCount = 0;\r
315\r
067ed98a
RN
316 Status = gBS->LocateHandleBuffer (\r
317 ByProtocol,\r
318 &gEfiDriverHealthProtocolGuid,\r
319 NULL,\r
320 &NumHandles,\r
321 &DriverHealthHandles\r
322 );\r
323\r
324 if (Status == EFI_NOT_FOUND || NumHandles == 0) {\r
325 //\r
326 // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND\r
327 //\r
328 return NULL;\r
329 }\r
330\r
331 ASSERT_EFI_ERROR (Status);\r
332 ASSERT (DriverHealthHandles != NULL);\r
333\r
334 //\r
335 // Check the health status of all controllers in the platform\r
336 // Start by looping through all the Driver Health Protocol handles in the handle database\r
337 //\r
338 for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) {\r
339 //\r
340 // Get the cumulative health status of the driver\r
341 //\r
342 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL);\r
343 if (EFI_ERROR (Status)) {\r
344 continue;\r
345 }\r
346\r
347 //\r
348 // See if the list of all handles in the handle database has been retrieved\r
349 //\r
350 //\r
351 if (Handles == NULL) {\r
352 //\r
353 // Retrieve the list of all handles from the handle database\r
354 //\r
355 Status = gBS->LocateHandleBuffer (\r
356 AllHandles,\r
357 NULL,\r
358 NULL,\r
359 &HandleCount,\r
360 &Handles\r
361 );\r
362 ASSERT_EFI_ERROR (Status);\r
363 }\r
364 //\r
365 // Loop through all the controller handles in the handle database\r
366 //\r
367 for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) {\r
368 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL);\r
369 if (EFI_ERROR (Status)) {\r
370 continue;\r
371 }\r
372\r
373 //\r
374 // Loop through all the child handles in the handle database\r
375 //\r
376 for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {\r
377 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]);\r
378 if (EFI_ERROR (Status)) {\r
379 continue;\r
380 }\r
381 }\r
382 }\r
383 }\r
384\r
385 Status = EFI_SUCCESS;\r
386\r
387 if (Handles != NULL) {\r
388 FreePool (Handles);\r
389 }\r
390 if (DriverHealthHandles != NULL) {\r
391 FreePool (DriverHealthHandles);\r
392 }\r
393\r
394 return DriverHealthInfo;\r
395}\r
396\r
397/**\r
398 Free the Driver Health information array.\r
399\r
400 @param DriverHealthInfo Pointer to array of the Driver Health information.\r
401 @param Count Count of the array.\r
402\r
403 @retval EFI_SUCCESS The array is freed.\r
404 @retval EFI_INVALID_PARAMETER The array is NULL.\r
405**/\r
406EFI_STATUS\r
407EFIAPI\r
408EfiBootManagerFreeDriverHealthInfo (\r
409 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo,\r
410 UINTN Count\r
411 )\r
412{\r
413 UINTN Index;\r
414\r
415 for (Index = 0; Index < Count; Index++) {\r
416 if (DriverHealthInfo[Index].MessageList != NULL) {\r
417 FreePool (DriverHealthInfo[Index].MessageList);\r
418 }\r
419 }\r
420 return gBS->FreePool (DriverHealthInfo);\r
421}\r
422\r
423/**\r
424 Repair all the controllers according to the Driver Health status queried.\r
425**/\r
426VOID\r
427BmRepairAllControllers (\r
428 VOID\r
429 )\r
430{\r
431 EFI_STATUS Status;\r
432 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;\r
433 EFI_DRIVER_HEALTH_STATUS HealthStatus;\r
434 UINTN Count;\r
435 UINTN Index;\r
436 BOOLEAN RepairRequired;\r
437 BOOLEAN ConfigurationRequired;\r
438 BOOLEAN ReconnectRequired;\r
439 BOOLEAN RebootRequired;\r
440 EFI_HII_HANDLE *HiiHandles;\r
441 EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2;\r
442\r
443 //\r
444 // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check.\r
445 //\r
446 if (CompareGuid (PcdGetPtr (PcdDriverHealthConfigureForm), &gZeroGuid)) {\r
447 return;\r
448 }\r
449\r
450 Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2);\r
451 ASSERT_EFI_ERROR (Status);\r
452\r
453 do {\r
454 RepairRequired = FALSE;\r
455 ConfigurationRequired = FALSE;\r
456\r
457 //\r
458 // Deal with Repair Required\r
459 //\r
460 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);\r
461 for (Index = 0; Index < Count; Index++) {\r
462 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) {\r
463 ConfigurationRequired = TRUE;\r
464 }\r
465 \r
466 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) {\r
467 RepairRequired = TRUE;\r
468\r
469 BmDisplayMessages (&DriverHealthInfo[Index]);\r
470\r
471 Status = DriverHealthInfo[Index].DriverHealth->Repair (\r
472 DriverHealthInfo[Index].DriverHealth,\r
473 DriverHealthInfo[Index].ControllerHandle,\r
474 DriverHealthInfo[Index].ChildHandle,\r
475 BmRepairNotify\r
476 );\r
477 if (!EFI_ERROR (Status) && !ConfigurationRequired) {\r
478 Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus (\r
479 DriverHealthInfo[Index].DriverHealth,\r
480 DriverHealthInfo[Index].ControllerHandle,\r
481 DriverHealthInfo[Index].ChildHandle,\r
482 &HealthStatus,\r
483 NULL,\r
484 NULL\r
485 );\r
486 if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) {\r
487 ConfigurationRequired = TRUE;\r
488 }\r
489 }\r
490 }\r
491 }\r
492\r
493 if (ConfigurationRequired) {\r
494 HiiHandles = HiiGetHiiHandles (NULL);\r
495 if (HiiHandles != NULL) {\r
496 for (Index = 0; HiiHandles[Index] != NULL; Index++) {\r
497 Status = FormBrowser2->SendForm (\r
498 FormBrowser2,\r
499 &HiiHandles[Index],\r
500 1,\r
501 PcdGetPtr (PcdDriverHealthConfigureForm),\r
502 0,\r
503 NULL,\r
504 NULL\r
505 );\r
506 if (!EFI_ERROR (Status)) {\r
507 break;\r
508 }\r
509 }\r
510 FreePool (HiiHandles);\r
511 }\r
512 }\r
513 \r
514 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);\r
515 } while (RepairRequired || ConfigurationRequired);\r
516\r
517 RebootRequired = FALSE;\r
518 ReconnectRequired = FALSE;\r
519 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);\r
520 for (Index = 0; Index < Count; Index++) {\r
521\r
522 BmDisplayMessages (&DriverHealthInfo[Index]);\r
523\r
524 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) {\r
525 Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL);\r
526 if (EFI_ERROR (Status)) {\r
527 //\r
528 // Disconnect failed. Need to promote reconnect to a reboot.\r
529 //\r
530 RebootRequired = TRUE;\r
531 } else {\r
532 gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE);\r
533 ReconnectRequired = TRUE;\r
534 }\r
535 }\r
536\r
537 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) {\r
538 RebootRequired = TRUE;\r
539 }\r
540 }\r
541 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);\r
542\r
543\r
544 if (ReconnectRequired) {\r
545 BmRepairAllControllers ();\r
546 }\r
547\r
548 DEBUG_CODE (\r
549 CHAR16 *ControllerName;\r
550\r
551 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);\r
552 for (Index = 0; Index < Count; Index++) {\r
553 ControllerName = BmGetControllerName (\r
554 DriverHealthInfo[Index].DriverHealthHandle,\r
555 DriverHealthInfo[Index].ControllerHandle,\r
556 DriverHealthInfo[Index].ChildHandle\r
557 );\r
558 DEBUG ((\r
559 EFI_D_INFO,\r
560 "%02d: %s - %s\n",\r
561 Index,\r
562 ControllerName,\r
563 mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus]\r
564 ));\r
565 if (ControllerName != NULL) {\r
566 FreePool (ControllerName);\r
567 }\r
568 }\r
569 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);\r
570 );\r
571\r
572 if (RebootRequired) {\r
573 DEBUG ((EFI_D_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n"));\r
574 gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);\r
575 }\r
576}\r