Update HiiConfigAccess.ExtractConfig interface to support NULL request string and...
[efi/edk2/.git] / edk2 / MdeModulePkg / Universal / Network / IScsiDxe / IScsiConfig.c
1 /** @file\r
2   Helper functions for configuring or getting the parameters relating to iSCSI.\r
3 \r
4 Copyright (c) 2004 - 2010, Intel Corporation.<BR>\r
5 All rights reserved. This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution.  The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9 \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 \r
13 **/\r
14 \r
15 #include "IScsiImpl.h"\r
16 \r
17 EFI_GUID        mVendorGuid              = ISCSI_CONFIG_GUID;\r
18 CHAR16          mVendorStorageName[]     = L"ISCSI_CONFIG_IFR_NVDATA";\r
19 BOOLEAN         mIScsiDeviceListUpdated  = FALSE;\r
20 UINTN           mNumberOfIScsiDevices    = 0;\r
21 ISCSI_FORM_CALLBACK_INFO  *mCallbackInfo = NULL;\r
22 \r
23 LIST_ENTRY      mIScsiConfigFormList = {\r
24   &mIScsiConfigFormList,\r
25   &mIScsiConfigFormList\r
26 };\r
27 \r
28 HII_VENDOR_DEVICE_PATH  mIScsiHiiVendorDevicePath = {\r
29   {\r
30     {\r
31       HARDWARE_DEVICE_PATH,\r
32       HW_VENDOR_DP,\r
33       {\r
34         (UINT8) (sizeof (VENDOR_DEVICE_PATH)),\r
35         (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)\r
36       }\r
37     },\r
38     //\r
39     // {49D7B73E-143D-4716-977B-C45F1CB038CC}\r
40     //\r
41     { 0x49d7b73e, 0x143d, 0x4716, { 0x97, 0x7b, 0xc4, 0x5f, 0x1c, 0xb0, 0x38, 0xcc } }\r
42   },\r
43   {\r
44     END_DEVICE_PATH_TYPE,\r
45     END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
46     { \r
47       (UINT8) (END_DEVICE_PATH_LENGTH),\r
48       (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)\r
49     }\r
50   }\r
51 };\r
52 \r
53 /**\r
54   Convert the IPv4 address into a dotted string.\r
55 \r
56   @param[in]   Ip   The IPv4 address.\r
57   @param[out]  Str  The dotted IP string.\r
58 **/\r
59 VOID\r
60 IScsiIpToStr (\r
61   IN  EFI_IPv4_ADDRESS  *Ip,\r
62   OUT CHAR16            *Str\r
63   )\r
64 {\r
65   UnicodeSPrint ( Str, 2 * IP4_STR_MAX_SIZE, L"%d.%d.%d.%d", Ip->Addr[0], Ip->Addr[1], Ip->Addr[2], Ip->Addr[3]);\r
66 }\r
67 \r
68 /**\r
69   Update the list of iSCSI devices the iSCSI driver is controlling.\r
70 \r
71   @retval EFI_SUCCESS            The callback successfully handled the action.\r
72   @retval Others                 Other errors as indicated.   \r
73 **/\r
74 EFI_STATUS\r
75 IScsiUpdateDeviceList (\r
76   VOID\r
77   )\r
78 {\r
79   EFI_STATUS                  Status;\r
80   ISCSI_DEVICE_LIST           *DeviceList;\r
81   UINTN                       DataSize;\r
82   UINTN                       NumHandles;\r
83   EFI_HANDLE                  *Handles;\r
84   UINTN                       HandleIndex;\r
85   UINTN                       Index;\r
86   UINTN                       LastDeviceIndex;\r
87   EFI_MAC_ADDRESS             MacAddress;\r
88   UINTN                       HwAddressSize;\r
89   UINT16                      VlanId;\r
90   ISCSI_MAC_INFO              *CurMacInfo;\r
91   ISCSI_MAC_INFO              TempMacInfo;\r
92   CHAR16                      MacString[70];\r
93   UINTN                       DeviceListSize;\r
94 \r
95   //\r
96   // Dump all the handles the Managed Network Service Binding Protocol is installed on.\r
97   //\r
98   Status = gBS->LocateHandleBuffer (\r
99                   ByProtocol,\r
100                   &gEfiManagedNetworkServiceBindingProtocolGuid,\r
101                   NULL,\r
102                   &NumHandles,\r
103                   &Handles\r
104                   );\r
105   if (EFI_ERROR (Status)) {\r
106     return Status;\r
107   }\r
108 \r
109   DataSize = 0;\r
110   Status = gRT->GetVariable (\r
111                   L"iSCSIDeviceList",\r
112                   &mVendorGuid,\r
113                   NULL,\r
114                   &DataSize,\r
115                   NULL\r
116                   );\r
117   if (Status == EFI_BUFFER_TOO_SMALL) {\r
118     DeviceList = (ISCSI_DEVICE_LIST *) AllocatePool (DataSize);\r
119 \r
120     gRT->GetVariable (\r
121           L"iSCSIDeviceList",\r
122           &mVendorGuid,\r
123           NULL,\r
124           &DataSize,\r
125           DeviceList\r
126           );\r
127 \r
128     LastDeviceIndex = 0;\r
129 \r
130     for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) {\r
131       Status = NetLibGetMacAddress (Handles[HandleIndex], &MacAddress, &HwAddressSize);\r
132       ASSERT (Status == EFI_SUCCESS);\r
133       VlanId = NetLibGetVlanId (Handles[HandleIndex]);\r
134 \r
135       for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {\r
136         CurMacInfo = &DeviceList->MacInfo[Index];\r
137         if ((CurMacInfo->Len == HwAddressSize) &&\r
138             (CurMacInfo->VlanId == VlanId) &&\r
139             (NET_MAC_EQUAL (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize))\r
140             ) {\r
141           //\r
142           // The previous configured NIC is still here.\r
143           //\r
144           if (Index != LastDeviceIndex) {\r
145             //\r
146             // Swap the current MAC address entry with the one indexed by\r
147             // LastDeviceIndex.\r
148             //\r
149             CopyMem (&TempMacInfo, CurMacInfo, sizeof (ISCSI_MAC_INFO));\r
150             CopyMem (CurMacInfo, &DeviceList->MacInfo[LastDeviceIndex], sizeof (ISCSI_MAC_INFO));\r
151             CopyMem (&DeviceList->MacInfo[LastDeviceIndex], &TempMacInfo, sizeof (ISCSI_MAC_INFO));\r
152           }\r
153 \r
154           LastDeviceIndex++;\r
155         }\r
156       }\r
157 \r
158       if (LastDeviceIndex == DeviceList->NumDevice) {\r
159         break;\r
160       }\r
161     }\r
162 \r
163     for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {\r
164       //\r
165       // delete the variables\r
166       //\r
167       CurMacInfo = &DeviceList->MacInfo[Index];\r
168       IScsiMacAddrToStr (&CurMacInfo->Mac, CurMacInfo->Len, CurMacInfo->VlanId, MacString);\r
169       gRT->SetVariable (MacString, &gEfiIScsiInitiatorNameProtocolGuid, 0, 0, NULL);\r
170       gRT->SetVariable (MacString, &mIScsiCHAPAuthInfoGuid, 0, 0, NULL);\r
171     }\r
172 \r
173     FreePool (DeviceList);\r
174   } else if (Status != EFI_NOT_FOUND) {\r
175     FreePool (Handles);\r
176     return Status;\r
177   }\r
178   //\r
179   // Construct the new iSCSI device list.\r
180   //\r
181   DeviceListSize        = sizeof (ISCSI_DEVICE_LIST) + (NumHandles - 1) * sizeof (ISCSI_MAC_INFO);\r
182   DeviceList            = (ISCSI_DEVICE_LIST *) AllocatePool (DeviceListSize);\r
183   DeviceList->NumDevice = (UINT8) NumHandles;\r
184 \r
185   for (Index = 0; Index < NumHandles; Index++) {\r
186     NetLibGetMacAddress (Handles[Index], &MacAddress, &HwAddressSize);\r
187 \r
188     CurMacInfo  = &DeviceList->MacInfo[Index];\r
189     CopyMem (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize);\r
190     CurMacInfo->Len = (UINT8) HwAddressSize;\r
191     CurMacInfo->VlanId = NetLibGetVlanId (Handles[Index]);\r
192   }\r
193 \r
194   gRT->SetVariable (\r
195         L"iSCSIDeviceList",\r
196         &mVendorGuid,\r
197         ISCSI_CONFIG_VAR_ATTR,\r
198         DeviceListSize,\r
199         DeviceList\r
200         );\r
201 \r
202   FreePool (DeviceList);\r
203   FreePool (Handles);\r
204 \r
205   return Status;\r
206 }\r
207 \r
208 /**\r
209   Get the iSCSI configuration form entry by the index of the goto opcode actived.\r
210 \r
211   @param[in]  Index The 0-based index of the goto opcode actived.\r
212 \r
213   @return The iSCSI configuration form entry found.\r
214 **/\r
215 ISCSI_CONFIG_FORM_ENTRY *\r
216 IScsiGetConfigFormEntryByIndex (\r
217   IN UINT32 Index\r
218   )\r
219 {\r
220   UINT32                  CurrentIndex;\r
221   LIST_ENTRY              *Entry;\r
222   ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry;\r
223 \r
224   CurrentIndex    = 0;\r
225   ConfigFormEntry = NULL;\r
226 \r
227   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {\r
228     if (CurrentIndex == Index) {\r
229       ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);\r
230       break;\r
231     }\r
232 \r
233     CurrentIndex++;\r
234   }\r
235 \r
236   return ConfigFormEntry;\r
237 }\r
238 \r
239 /**\r
240   Convert the iSCSI configuration data into the IFR data.\r
241 \r
242   @param[in]   ConfigFormEntry The iSCSI configuration form entry.\r
243   @param[out]  IfrNvData       The IFR nv data.\r
244 **/\r
245 VOID\r
246 IScsiConvertDeviceConfigDataToIfrNvData (\r
247   IN ISCSI_CONFIG_FORM_ENTRY      *ConfigFormEntry,\r
248   OUT ISCSI_CONFIG_IFR_NVDATA     *IfrNvData\r
249   )\r
250 {\r
251   ISCSI_SESSION_CONFIG_NVDATA   *SessionConfigData;\r
252   ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;\r
253 \r
254   //\r
255   // Normal session configuration parameters.\r
256   //\r
257   SessionConfigData                 = &ConfigFormEntry->SessionConfigData;\r
258   IfrNvData->Enabled                = SessionConfigData->Enabled;\r
259 \r
260   IfrNvData->InitiatorInfoFromDhcp  = SessionConfigData->InitiatorInfoFromDhcp;\r
261   IfrNvData->TargetInfoFromDhcp     = SessionConfigData->TargetInfoFromDhcp;\r
262   IfrNvData->TargetPort             = SessionConfigData->TargetPort;\r
263 \r
264   IScsiIpToStr (&SessionConfigData->LocalIp, IfrNvData->LocalIp);\r
265   IScsiIpToStr (&SessionConfigData->SubnetMask, IfrNvData->SubnetMask);\r
266   IScsiIpToStr (&SessionConfigData->Gateway, IfrNvData->Gateway);\r
267   IScsiIpToStr (&SessionConfigData->TargetIp, IfrNvData->TargetIp);\r
268 \r
269   IScsiAsciiStrToUnicodeStr (SessionConfigData->TargetName, IfrNvData->TargetName);\r
270 \r
271   IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun);\r
272 \r
273   //\r
274   // CHAP authentication parameters.\r
275   //\r
276   AuthConfigData      = &ConfigFormEntry->AuthConfigData;\r
277 \r
278   IfrNvData->CHAPType = AuthConfigData->CHAPType;\r
279 \r
280   IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPName, IfrNvData->CHAPName);\r
281   IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPSecret, IfrNvData->CHAPSecret);\r
282   IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPName, IfrNvData->ReverseCHAPName);\r
283   IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPSecret, IfrNvData->ReverseCHAPSecret);\r
284 }\r
285 \r
286 /**\r
287   This function allows the caller to request the current\r
288   configuration for one or more named elements. The resulting\r
289   string is in <ConfigAltResp> format. Any and all alternative\r
290   configuration strings shall also be appended to the end of the\r
291   current configuration string. If they are, they must appear\r
292   after the current configuration. They must contain the same\r
293   routing (GUID, NAME, PATH) as the current configuration string.\r
294   They must have an additional description indicating the type of\r
295   alternative configuration the string represents,\r
296   "ALTCFG=<StringToken>". That <StringToken> (when\r
297   converted from Hex UNICODE to binary) is a reference to a\r
298   string in the associated string pack.\r
299 \r
300   @param[in] This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
301   @param[in] Request    A null-terminated Unicode string in\r
302                         <ConfigRequest> format. Note that this\r
303                         includes the routing information as well as\r
304                         the configurable name / value pairs. It is\r
305                         invalid for this string to be in\r
306                         <MultiConfigRequest> format.\r
307   @param[out] Progress  On return, points to a character in the\r
308                         Request string. Points to the string's null\r
309                         terminator if request was successful. Points\r
310                         to the most recent "&" before the first\r
311                         failing name / value pair (or the beginning\r
312                         of the string if the failure is in the first\r
313                         name / value pair) if the request was not\r
314                         successful.\r
315   @param[out] Results   A null-terminated Unicode string in\r
316                         <ConfigAltResp> format which has all values\r
317                         filled in for the names in the Request string.\r
318                         String to be allocated by the called function.\r
319 \r
320   @retval EFI_SUCCESS             The Results string is filled with the\r
321                                   values corresponding to all requested\r
322                                   names.\r
323   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
324                                   parts of the results that must be\r
325                                   stored awaiting possible future\r
326                                   protocols.\r
327   @retval EFI_INVALID_PARAMETER   For example, passing in a NULL\r
328                                   for the Request parameter\r
329                                   would result in this type of\r
330                                   error. In this case, the\r
331                                   Progress parameter would be\r
332                                   set to NULL. \r
333   @retval EFI_NOT_FOUND           Routing data doesn't match any\r
334                                   known driver. Progress set to the\r
335                                   first character in the routing header.\r
336                                   Note: There is no requirement that the\r
337                                   driver validate the routing data. It\r
338                                   must skip the <ConfigHdr> in order to\r
339                                   process the names.\r
340   @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set\r
341                                   to most recent & before the\r
342                                   error or the beginning of the\r
343                                   string.\r
344   @retval EFI_INVALID_PARAMETER   Unknown name. Progress points\r
345                                   to the & before the name in\r
346                                   question.Currently not implemented.\r
347 **/\r
348 EFI_STATUS\r
349 EFIAPI\r
350 IScsiFormExtractConfig (\r
351   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
352   IN  CONST EFI_STRING                       Request,\r
353   OUT EFI_STRING                             *Progress,\r
354   OUT EFI_STRING                             *Results\r
355   )\r
356 {\r
357   EFI_STATUS                       Status;\r
358   CHAR8                            InitiatorName[ISCSI_NAME_IFR_MAX_SIZE];\r
359   UINTN                            BufferSize;\r
360   ISCSI_CONFIG_IFR_NVDATA          *IfrNvData;\r
361   ISCSI_FORM_CALLBACK_INFO         *Private;\r
362   EFI_HII_CONFIG_ROUTING_PROTOCOL  *HiiConfigRouting;\r
363   EFI_STRING                       ConfigRequestHdr;\r
364   EFI_STRING                       ConfigRequest;\r
365   BOOLEAN                          AllocatedRequest;\r
366   UINTN                            Size;\r
367 \r
368   if (Progress == NULL || Results == NULL) {\r
369     return EFI_INVALID_PARAMETER;\r
370   }\r
371 \r
372   *Progress = Request;\r
373   if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &mVendorGuid, mVendorStorageName)) {\r
374     return EFI_NOT_FOUND;\r
375   }\r
376 \r
377   ConfigRequestHdr = NULL;\r
378   ConfigRequest    = NULL;\r
379   AllocatedRequest = FALSE;\r
380   Size             = 0;\r
381 \r
382   if (!mIScsiDeviceListUpdated) {\r
383     //\r
384     // Update the device list.\r
385     //\r
386     IScsiUpdateDeviceList ();\r
387     mIScsiDeviceListUpdated = TRUE;\r
388   }\r
389 \r
390   Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);\r
391   IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));\r
392   ASSERT (IfrNvData != NULL);\r
393   if (Private->Current != NULL) {\r
394     IScsiConvertDeviceConfigDataToIfrNvData (Private->Current, IfrNvData);\r
395   }\r
396 \r
397   BufferSize  = ISCSI_NAME_IFR_MAX_SIZE;\r
398   Status      = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);\r
399   if (EFI_ERROR (Status)) {\r
400     IfrNvData->InitiatorName[0] = L'\0';\r
401   } else {\r
402     IScsiAsciiStrToUnicodeStr (InitiatorName, IfrNvData->InitiatorName);\r
403   }\r
404 \r
405   //\r
406   // Convert buffer data to <ConfigResp> by helper function BlockToConfig()\r
407   //\r
408   HiiConfigRouting = Private->ConfigRouting;\r
409   BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);\r
410   ConfigRequest = Request;\r
411   if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {\r
412     //\r
413     // Request has no request element, construct full request string.\r
414     // Allocate and fill a buffer large enough to hold the <ConfigHdr> template\r
415     // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator\r
416     //\r
417     ConfigRequestHdr = HiiConstructConfigHdr (&mVendorGuid, mVendorStorageName, Private->DriverHandle);\r
418     Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);\r
419     ConfigRequest = AllocateZeroPool (Size);\r
420     ASSERT (ConfigRequest != NULL);\r
421     AllocatedRequest = TRUE;\r
422     UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);\r
423     FreePool (ConfigRequestHdr);\r
424   }\r
425   Status = HiiConfigRouting->BlockToConfig (\r
426                                HiiConfigRouting,\r
427                                ConfigRequest,\r
428                                (UINT8 *) IfrNvData,\r
429                                BufferSize,\r
430                                Results,\r
431                                Progress\r
432                                );\r
433   FreePool (IfrNvData);\r
434   //\r
435   // Free the allocated config request string.\r
436   //\r
437   if (AllocatedRequest) {\r
438     FreePool (ConfigRequest);\r
439     ConfigRequest = NULL;\r
440   }\r
441 \r
442   //\r
443   // Set Progress string to the original request string.\r
444   //\r
445   if (Request == NULL) {\r
446     *Progress = NULL;\r
447   } else if (StrStr (Request, L"OFFSET") == NULL) {\r
448     *Progress = Request + StrLen (Request);\r
449   }\r
450 \r
451   return Status;\r
452 }\r
453 \r
454 /**\r
455   This function applies changes in a driver's configuration.\r
456   Input is a Configuration, which has the routing data for this\r
457   driver followed by name / value configuration pairs. The driver\r
458   must apply those pairs to its configurable storage. If the\r
459   driver's configuration is stored in a linear block of data\r
460   and the driver's name / value pairs are in <BlockConfig>\r
461   format, it may use the ConfigToBlock helper function (above) to\r
462   simplify the job. Currently not implemented.\r
463 \r
464   @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
465   @param[in]  Configuration  A null-terminated Unicode string in\r
466                              <ConfigString> format.   \r
467   @param[out] Progress       A pointer to a string filled in with the\r
468                              offset of the most recent '&' before the\r
469                              first failing name / value pair (or the\r
470                              beginn ing of the string if the failure\r
471                              is in the first name / value pair) or\r
472                              the terminating NULL if all was\r
473                              successful.\r
474 \r
475   @retval EFI_SUCCESS             The results have been distributed or are\r
476                                   awaiting distribution.  \r
477   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
478                                   parts of the results that must be\r
479                                   stored awaiting possible future\r
480                                   protocols.\r
481   @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the\r
482                                   Results parameter would result\r
483                                   in this type of error.\r
484   @retval EFI_NOT_FOUND           Target for the specified routing data\r
485                                   was not found.\r
486 **/\r
487 EFI_STATUS\r
488 EFIAPI\r
489 IScsiFormRouteConfig (\r
490   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
491   IN  CONST EFI_STRING                       Configuration,\r
492   OUT EFI_STRING                             *Progress\r
493   )\r
494 {\r
495   if (Configuration == NULL || Progress == NULL) {\r
496     return EFI_INVALID_PARAMETER;\r
497   }\r
498 \r
499   //\r
500   // Check routing data in <ConfigHdr>.\r
501   // Note: if only one Storage is used, then this checking could be skipped.\r
502   //\r
503   if (!HiiIsConfigHdrMatch (Configuration, &mVendorGuid, mVendorStorageName)) {\r
504     *Progress = Configuration;\r
505     return EFI_NOT_FOUND;\r
506   }\r
507 \r
508   *Progress = Configuration + StrLen (Configuration);\r
509   return EFI_SUCCESS;\r
510 }\r
511 \r
512 /**\r
513   This function is called to provide results data to the driver.\r
514   This data consists of a unique key that is used to identify\r
515   which data is either being passed back or being asked for.\r
516 \r
517   @param[in]  This               Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
518   @param[in]  Action             Specifies the type of action taken by the browser.\r
519   @param[in]  QuestionId         A unique value which is sent to the original\r
520                                  exporting driver so that it can identify the type\r
521                                  of data to expect. The format of the data tends to \r
522                                  vary based on the opcode that enerated the callback.\r
523   @param[in]  Type               The type of value for the question.\r
524   @param[in]  Value              A pointer to the data being sent to the original\r
525                                  exporting driver.\r
526   @param[out]  ActionRequest     On return, points to the action requested by the\r
527                                  callback function.\r
528 \r
529   @retval EFI_SUCCESS            The callback successfully handled the action.\r
530   @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the\r
531                                  variable and its data.\r
532   @retval EFI_DEVICE_ERROR       The variable could not be saved.\r
533   @retval EFI_UNSUPPORTED        The specified Action is not supported by the\r
534                                  callback.Currently not implemented.\r
535   @retval EFI_INVALID_PARAMETERS Passing in wrong parameter. \r
536   @retval Others                 Other errors as indicated. \r
537 **/\r
538 EFI_STATUS\r
539 EFIAPI\r
540 IScsiFormCallback (\r
541   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
542   IN  EFI_BROWSER_ACTION                     Action,\r
543   IN  EFI_QUESTION_ID                        QuestionId,\r
544   IN  UINT8                                  Type,\r
545   IN  EFI_IFR_TYPE_VALUE                     *Value,\r
546   OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest\r
547   )\r
548 {\r
549   ISCSI_FORM_CALLBACK_INFO  *Private;\r
550   UINTN                     BufferSize;\r
551   CHAR8                     IScsiName[ISCSI_NAME_IFR_MAX_SIZE];\r
552   CHAR16                    PortString[128];\r
553   CHAR8                     Ip4String[IP4_STR_MAX_SIZE];\r
554   CHAR8                     LunString[ISCSI_LUN_STR_MAX_LEN];\r
555   UINT64                    Lun;\r
556   EFI_STRING_ID             DeviceFormTitleToken;\r
557   ISCSI_CONFIG_IFR_NVDATA   *IfrNvData;\r
558   ISCSI_CONFIG_FORM_ENTRY   *ConfigFormEntry;\r
559   EFI_IP_ADDRESS            HostIp;\r
560   EFI_IP_ADDRESS            SubnetMask;\r
561   EFI_IP_ADDRESS            Gateway;\r
562   EFI_STATUS                Status;\r
563   EFI_INPUT_KEY             Key;\r
564 \r
565   Private   = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);\r
566 \r
567   //\r
568   // Retrive uncommitted data from Browser\r
569   //\r
570   IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));\r
571   ASSERT (IfrNvData != NULL);\r
572   if (!HiiGetBrowserData (&mVendorGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData)) {\r
573     FreePool (IfrNvData);\r
574     return EFI_NOT_FOUND;\r
575   }\r
576   \r
577   Status = EFI_SUCCESS;\r
578 \r
579   switch (QuestionId) {\r
580   case KEY_INITIATOR_NAME:\r
581     IScsiUnicodeStrToAsciiStr (IfrNvData->InitiatorName, IScsiName);\r
582     BufferSize  = AsciiStrLen (IScsiName) + 1;\r
583 \r
584     Status      = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName);\r
585     if (EFI_ERROR (Status)) {\r
586       CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL);\r
587     }\r
588 \r
589     break;\r
590 \r
591   case KEY_LOCAL_IP:\r
592     IScsiUnicodeStrToAsciiStr (IfrNvData->LocalIp, Ip4String);\r
593     Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);\r
594     if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {\r
595       CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);\r
596       Status = EFI_INVALID_PARAMETER;\r
597     } else {\r
598       CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));\r
599     }\r
600 \r
601     break;\r
602 \r
603   case KEY_SUBNET_MASK:\r
604     IScsiUnicodeStrToAsciiStr (IfrNvData->SubnetMask, Ip4String);\r
605     Status = IScsiAsciiStrToIp (Ip4String, &SubnetMask.v4);\r
606     if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {\r
607       CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL);\r
608       Status = EFI_INVALID_PARAMETER;\r
609     } else {\r
610       CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));\r
611     }\r
612 \r
613     break;\r
614 \r
615   case KEY_GATE_WAY:\r
616     IScsiUnicodeStrToAsciiStr (IfrNvData->Gateway, Ip4String);\r
617     Status = IScsiAsciiStrToIp (Ip4String, &Gateway.v4);\r
618     if (EFI_ERROR (Status) || ((Gateway.Addr[0] != 0) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 0))) {\r
619       CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL);\r
620       Status = EFI_INVALID_PARAMETER;\r
621     } else {\r
622       CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));\r
623     }\r
624 \r
625     break;\r
626 \r
627   case KEY_TARGET_IP:\r
628     IScsiUnicodeStrToAsciiStr (IfrNvData->TargetIp, Ip4String);\r
629     Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);\r
630     if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {\r
631       CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);\r
632       Status = EFI_INVALID_PARAMETER;\r
633     } else {\r
634       CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp.v4, sizeof (HostIp.v4));\r
635     }\r
636 \r
637     break;\r
638 \r
639   case KEY_TARGET_NAME:\r
640     IScsiUnicodeStrToAsciiStr (IfrNvData->TargetName, IScsiName);\r
641     Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName));\r
642     if (EFI_ERROR (Status)) {\r
643       CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL);\r
644     } else {\r
645       AsciiStrCpy (Private->Current->SessionConfigData.TargetName, IScsiName);\r
646     }\r
647 \r
648     break;\r
649 \r
650   case KEY_DHCP_ENABLE:\r
651     if (IfrNvData->InitiatorInfoFromDhcp == 0) {\r
652       IfrNvData->TargetInfoFromDhcp = 0;\r
653     }\r
654 \r
655     break;\r
656 \r
657   case KEY_BOOT_LUN:\r
658     IScsiUnicodeStrToAsciiStr (IfrNvData->BootLun, LunString);\r
659     Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);\r
660     if (EFI_ERROR (Status)) {\r
661       CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid LUN string!", NULL);\r
662     } else {\r
663       CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun));\r
664     }\r
665 \r
666     break;\r
667 \r
668   case KEY_CHAP_NAME:\r
669     IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPName, Private->Current->AuthConfigData.CHAPName);\r
670     break;\r
671 \r
672   case KEY_CHAP_SECRET:\r
673     IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPSecret, Private->Current->AuthConfigData.CHAPSecret);\r
674     break;\r
675 \r
676   case KEY_REVERSE_CHAP_NAME:\r
677     IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPName, Private->Current->AuthConfigData.ReverseCHAPName);\r
678     break;\r
679 \r
680   case KEY_REVERSE_CHAP_SECRET:\r
681     IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPSecret, Private->Current->AuthConfigData.ReverseCHAPSecret);\r
682     break;\r
683 \r
684   case KEY_SAVE_CHANGES:\r
685     //\r
686     // First, update those fields which don't have INTERACTIVE set.\r
687     //\r
688     Private->Current->SessionConfigData.Enabled               = IfrNvData->Enabled;\r
689     Private->Current->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;\r
690     Private->Current->SessionConfigData.TargetPort            = IfrNvData->TargetPort;\r
691     if (Private->Current->SessionConfigData.TargetPort == 0) {\r
692       Private->Current->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;\r
693     }\r
694 \r
695     Private->Current->SessionConfigData.TargetInfoFromDhcp  = IfrNvData->TargetInfoFromDhcp;\r
696     Private->Current->AuthConfigData.CHAPType               = IfrNvData->CHAPType;\r
697 \r
698     //\r
699     // Only do full parameter validation if iSCSI is enabled on this device.\r
700     //\r
701     if (Private->Current->SessionConfigData.Enabled) {\r
702       //\r
703       // Validate the address configuration of the Initiator if DHCP isn't\r
704       // deployed.\r
705       //\r
706       if (!Private->Current->SessionConfigData.InitiatorInfoFromDhcp) {\r
707         CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.LocalIp, sizeof (HostIp.v4));\r
708         CopyMem (&SubnetMask.v4, &Private->Current->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));\r
709         CopyMem (&Gateway.v4, &Private->Current->SessionConfigData.Gateway, sizeof (Gateway.v4));\r
710 \r
711         if ((Gateway.Addr[0] != 0)) {\r
712           if (SubnetMask.Addr[0] == 0) {\r
713             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Gateway address is set but subnet mask is zero.", NULL);\r
714             Status = EFI_INVALID_PARAMETER;\r
715             break;\r
716           } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) {\r
717             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Local IP and Gateway are not in the same subnet.", NULL);\r
718             Status = EFI_INVALID_PARAMETER;\r
719             break;\r
720           }\r
721         }\r
722       }\r
723       //\r
724       // Validate target configuration if DHCP isn't deployed.\r
725       //\r
726       if (!Private->Current->SessionConfigData.TargetInfoFromDhcp) {\r
727         CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.TargetIp, sizeof (HostIp.v4));\r
728         if (!NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {\r
729           CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Target IP is invalid!", NULL);\r
730           Status = EFI_INVALID_PARAMETER;\r
731           break;\r
732         }\r
733       }\r
734 \r
735       if (IfrNvData->CHAPType != ISCSI_CHAP_NONE) {\r
736         if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) {\r
737           CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"CHAP Name or CHAP Secret is invalid!", NULL);\r
738           Status = EFI_INVALID_PARAMETER;\r
739           break;\r
740         }\r
741 \r
742         if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) &&\r
743             ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0'))\r
744             ) {\r
745           CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Reverse CHAP Name or Reverse CHAP Secret is invalid!", NULL);\r
746           Status = EFI_INVALID_PARAMETER;\r
747           break;\r
748         }\r
749       }\r
750     }\r
751 \r
752     BufferSize = sizeof (Private->Current->SessionConfigData);\r
753     gRT->SetVariable (\r
754           Private->Current->MacString,\r
755           &gEfiIScsiInitiatorNameProtocolGuid,\r
756           ISCSI_CONFIG_VAR_ATTR,\r
757           BufferSize,\r
758           &Private->Current->SessionConfigData\r
759           );\r
760 \r
761     BufferSize = sizeof (Private->Current->AuthConfigData);\r
762     gRT->SetVariable (\r
763           Private->Current->MacString,\r
764           &mIScsiCHAPAuthInfoGuid,\r
765           ISCSI_CONFIG_VAR_ATTR,\r
766           BufferSize,\r
767           &Private->Current->AuthConfigData\r
768           );\r
769     *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
770     break;\r
771 \r
772   default:\r
773     if ((QuestionId >= KEY_DEVICE_ENTRY_BASE) && (QuestionId < (mNumberOfIScsiDevices + KEY_DEVICE_ENTRY_BASE))) {\r
774       //\r
775       // In case goto the device configuration form, update the device form title.\r
776       //\r
777       ConfigFormEntry = IScsiGetConfigFormEntryByIndex ((UINT32) (QuestionId - KEY_DEVICE_ENTRY_BASE));\r
778       ASSERT (ConfigFormEntry != NULL);\r
779 \r
780       UnicodeSPrint (PortString, (UINTN) 128, L"Port %s", ConfigFormEntry->MacString);\r
781       DeviceFormTitleToken = (EFI_STRING_ID) STR_ISCSI_DEVICE_FORM_TITLE;\r
782       HiiSetString (Private->RegisteredHandle, DeviceFormTitleToken, PortString, NULL);\r
783 \r
784       IScsiConvertDeviceConfigDataToIfrNvData (ConfigFormEntry, IfrNvData);\r
785 \r
786       Private->Current = ConfigFormEntry;\r
787     }\r
788 \r
789     break;\r
790   }\r
791 \r
792   if (!EFI_ERROR (Status)) {\r
793     //\r
794     // Pass changed uncommitted data back to Form Browser\r
795     //\r
796     HiiSetBrowserData (&mVendorGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData, NULL);\r
797   }\r
798 \r
799   FreePool (IfrNvData);\r
800   return Status;\r
801 }\r
802 \r
803 /**\r
804   Updates the iSCSI configuration form to add/delete an entry for the iSCSI\r
805   device specified by the Controller.\r
806 \r
807   @param[in]  DriverBindingHandle The driverbinding handle.\r
808   @param[in]  Controller          The controller handle of the iSCSI device.\r
809   @param[in]  AddForm             Whether to add or delete a form entry.\r
810 \r
811   @retval EFI_SUCCESS             The iSCSI configuration form is updated.\r
812   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
813   @retval Others                  Other errors as indicated.\r
814 **/\r
815 EFI_STATUS\r
816 IScsiConfigUpdateForm (\r
817   IN EFI_HANDLE  DriverBindingHandle,\r
818   IN EFI_HANDLE  Controller,\r
819   IN BOOLEAN     AddForm\r
820   )\r
821 {\r
822   LIST_ENTRY                  *Entry;\r
823   ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;\r
824   BOOLEAN                     EntryExisted;\r
825   EFI_STATUS                  Status;\r
826   EFI_MAC_ADDRESS             MacAddress;\r
827   UINTN                       HwAddressSize;\r
828   UINT16                      VlanId;\r
829   CHAR16                      PortString[128];\r
830   UINT16                      FormIndex;\r
831   UINTN                       BufferSize;\r
832   VOID                        *StartOpCodeHandle;\r
833   VOID                        *EndOpCodeHandle;\r
834   EFI_IFR_GUID_LABEL          *StartLabel;\r
835   EFI_IFR_GUID_LABEL          *EndLabel;\r
836 \r
837   ConfigFormEntry = NULL;\r
838   EntryExisted    = FALSE;\r
839 \r
840   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {\r
841     ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);\r
842 \r
843     if (ConfigFormEntry->Controller == Controller) {\r
844       EntryExisted = TRUE;\r
845       break;\r
846     }\r
847   }\r
848 \r
849   if (AddForm) {\r
850     if (EntryExisted) {\r
851       return EFI_SUCCESS;\r
852     } else {\r
853       //\r
854       // Add a new form.\r
855       //\r
856       ConfigFormEntry = (ISCSI_CONFIG_FORM_ENTRY *) AllocateZeroPool (sizeof (ISCSI_CONFIG_FORM_ENTRY));\r
857       if (ConfigFormEntry == NULL) {\r
858         return EFI_OUT_OF_RESOURCES;\r
859       }\r
860 \r
861       InitializeListHead (&ConfigFormEntry->Link);\r
862       ConfigFormEntry->Controller = Controller;\r
863 \r
864       //\r
865       // Get the MAC address and convert it into the formatted string.\r
866       //\r
867       Status = NetLibGetMacAddress (Controller, &MacAddress, &HwAddressSize);\r
868       ASSERT (Status == EFI_SUCCESS);\r
869       VlanId = NetLibGetVlanId (Controller);\r
870 \r
871       IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, ConfigFormEntry->MacString);\r
872 \r
873       //\r
874       // Get the normal session configuration data.\r
875       //\r
876       BufferSize = sizeof (ConfigFormEntry->SessionConfigData);\r
877       Status = gRT->GetVariable (\r
878                       ConfigFormEntry->MacString,\r
879                       &gEfiIScsiInitiatorNameProtocolGuid,\r
880                       NULL,\r
881                       &BufferSize,\r
882                       &ConfigFormEntry->SessionConfigData\r
883                       );\r
884       if (EFI_ERROR (Status)) {\r
885         ZeroMem (&ConfigFormEntry->SessionConfigData, sizeof (ConfigFormEntry->SessionConfigData));\r
886       }\r
887       //\r
888       // Get the CHAP authentication configuration data.\r
889       //\r
890       BufferSize = sizeof (ConfigFormEntry->AuthConfigData);\r
891       Status = gRT->GetVariable (\r
892                       ConfigFormEntry->MacString,\r
893                       &mIScsiCHAPAuthInfoGuid,\r
894                       NULL,\r
895                       &BufferSize,\r
896                       &ConfigFormEntry->AuthConfigData\r
897                       );\r
898       if (EFI_ERROR (Status)) {\r
899         ZeroMem (&ConfigFormEntry->AuthConfigData, sizeof (ConfigFormEntry->AuthConfigData));\r
900       }\r
901       //\r
902       // Compose the Port string and create a new EFI_STRING_ID.\r
903       //\r
904       UnicodeSPrint (PortString, 128, L"Port %s", ConfigFormEntry->MacString);\r
905       ConfigFormEntry->PortTitleToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);\r
906 \r
907       //\r
908       // Compose the help string of this port and create a new EFI_STRING_ID.\r
909       //\r
910       UnicodeSPrint (PortString, 128, L"Set the iSCSI parameters on port %s", ConfigFormEntry->MacString);\r
911       ConfigFormEntry->PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);\r
912 \r
913       InsertTailList (&mIScsiConfigFormList, &ConfigFormEntry->Link);\r
914       mNumberOfIScsiDevices++;\r
915     }\r
916   } else {\r
917     ASSERT (EntryExisted);\r
918 \r
919     mNumberOfIScsiDevices--;\r
920     RemoveEntryList (&ConfigFormEntry->Link);\r
921     FreePool (ConfigFormEntry);\r
922   }\r
923   //\r
924   // Allocate space for creation of Buffer\r
925   //\r
926 \r
927   //\r
928   // Init OpCode Handle\r
929   //\r
930   StartOpCodeHandle = HiiAllocateOpCodeHandle ();\r
931   ASSERT (StartOpCodeHandle != NULL);\r
932 \r
933   EndOpCodeHandle = HiiAllocateOpCodeHandle ();\r
934   ASSERT (EndOpCodeHandle != NULL);\r
935 \r
936   //\r
937   // Create Hii Extend Label OpCode as the start opcode\r
938   //\r
939   StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));\r
940   StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
941   StartLabel->Number       = DEVICE_ENTRY_LABEL;\r
942 \r
943   //\r
944   // Create Hii Extend Label OpCode as the end opcode\r
945   //\r
946   EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));\r
947   EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
948   EndLabel->Number       = LABEL_END;\r
949 \r
950   FormIndex = 0;\r
951   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {\r
952     ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);\r
953 \r
954     HiiCreateGotoOpCode (\r
955       StartOpCodeHandle,                            // Container for dynamic created opcodes\r
956       FORMID_DEVICE_FORM,                           // Target Form ID\r
957       ConfigFormEntry->PortTitleToken,              // Prompt text\r
958       ConfigFormEntry->PortTitleHelpToken,          // Help text\r
959       EFI_IFR_FLAG_CALLBACK,                        // Question flag\r
960       (UINT16)(KEY_DEVICE_ENTRY_BASE + FormIndex)   // Question ID\r
961       );\r
962 \r
963     FormIndex++;\r
964   }\r
965 \r
966   HiiUpdateForm (\r
967     mCallbackInfo->RegisteredHandle,\r
968     &mVendorGuid,\r
969     FORMID_MAIN_FORM,\r
970     StartOpCodeHandle, // Label DEVICE_ENTRY_LABEL\r
971     EndOpCodeHandle    // LABEL_END\r
972     );\r
973 \r
974   HiiFreeOpCodeHandle (StartOpCodeHandle);\r
975   HiiFreeOpCodeHandle (EndOpCodeHandle);\r
976 \r
977   return EFI_SUCCESS;\r
978 }\r
979 \r
980 /**\r
981   Initialize the iSCSI configuration form.\r
982 \r
983   @param[in]  DriverBindingHandle  The iSCSI driverbinding handle.\r
984 \r
985   @retval EFI_SUCCESS              The iSCSI configuration form is initialized.\r
986   @retval EFI_OUT_OF_RESOURCES     Failed to allocate memory.\r
987   @retval Others                   Other errors as indicated.\r
988 **/\r
989 EFI_STATUS\r
990 IScsiConfigFormInit (\r
991   VOID\r
992   )\r
993 {\r
994   EFI_STATUS                  Status;\r
995   EFI_HII_DATABASE_PROTOCOL   *HiiDatabase;\r
996   ISCSI_FORM_CALLBACK_INFO    *CallbackInfo;\r
997 \r
998   Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **)&HiiDatabase);\r
999   if (EFI_ERROR (Status)) {\r
1000     return Status;\r
1001   }\r
1002 \r
1003   CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO));\r
1004   if (CallbackInfo == NULL) {\r
1005     return EFI_OUT_OF_RESOURCES;\r
1006   }\r
1007 \r
1008   CallbackInfo->Signature   = ISCSI_FORM_CALLBACK_INFO_SIGNATURE;\r
1009   CallbackInfo->HiiDatabase = HiiDatabase;\r
1010   CallbackInfo->Current     = NULL;\r
1011 \r
1012   CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig;\r
1013   CallbackInfo->ConfigAccess.RouteConfig = IScsiFormRouteConfig;\r
1014   CallbackInfo->ConfigAccess.Callback = IScsiFormCallback;\r
1015 \r
1016   Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **)&CallbackInfo->ConfigRouting);\r
1017   if (EFI_ERROR (Status)) {\r
1018     FreePool(CallbackInfo);\r
1019     return Status;\r
1020   }\r
1021 \r
1022   //\r
1023   // Install Device Path Protocol and Config Access protocol to driver handle\r
1024   //\r
1025   Status = gBS->InstallMultipleProtocolInterfaces (\r
1026                   &CallbackInfo->DriverHandle,\r
1027                   &gEfiDevicePathProtocolGuid,\r
1028                   &mIScsiHiiVendorDevicePath,\r
1029                   &gEfiHiiConfigAccessProtocolGuid,\r
1030                   &CallbackInfo->ConfigAccess,\r
1031                   NULL\r
1032                   );\r
1033   ASSERT_EFI_ERROR (Status);\r
1034   \r
1035   //\r
1036   // Publish our HII data\r
1037   //\r
1038   CallbackInfo->RegisteredHandle = HiiAddPackages (\r
1039                                      &mVendorGuid,\r
1040                                      CallbackInfo->DriverHandle,\r
1041                                      IScsiDxeStrings,\r
1042                                      IScsiConfigDxeBin,\r
1043                                      NULL\r
1044                                      );\r
1045   if (CallbackInfo->RegisteredHandle == NULL) {\r
1046     FreePool(CallbackInfo);\r
1047     return EFI_OUT_OF_RESOURCES;\r
1048   }\r
1049 \r
1050   mCallbackInfo = CallbackInfo;\r
1051 \r
1052   return Status;\r
1053 }\r
1054 \r
1055 /**\r
1056   Unload the iSCSI configuration form, this includes: delete all the iSCSI\r
1057   device configuration entries, uninstall the form callback protocol and\r
1058   free the resources used.\r
1059 \r
1060   @param[in]  DriverBindingHandle The iSCSI driverbinding handle.\r
1061   \r
1062   @retval EFI_SUCCESS             The iSCSI configuration form is unloaded.\r
1063   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
1064 **/\r
1065 EFI_STATUS\r
1066 IScsiConfigFormUnload (\r
1067   IN EFI_HANDLE  DriverBindingHandle\r
1068   )\r
1069 {\r
1070   ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;\r
1071 \r
1072   while (!IsListEmpty (&mIScsiConfigFormList)) {\r
1073     //\r
1074     // Uninstall the device forms as the iSCSI driver instance may fail to\r
1075     // control the controller but still install the device configuration form.\r
1076     // In such case, upon driver unloading, the driver instance's driverbinding.\r
1077     // stop () won't be called, so we have to take this chance here to uninstall\r
1078     // the device form.\r
1079     //\r
1080     ConfigFormEntry = NET_LIST_USER_STRUCT (mIScsiConfigFormList.ForwardLink, ISCSI_CONFIG_FORM_ENTRY, Link);\r
1081     IScsiConfigUpdateForm (DriverBindingHandle, ConfigFormEntry->Controller, FALSE);\r
1082   }\r
1083 \r
1084   //\r
1085   // Remove HII package list\r
1086   //\r
1087   mCallbackInfo->HiiDatabase->RemovePackageList (\r
1088                                 mCallbackInfo->HiiDatabase,\r
1089                                 mCallbackInfo->RegisteredHandle\r
1090                                 );\r
1091 \r
1092   //\r
1093   // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL\r
1094   //\r
1095   gBS->UninstallMultipleProtocolInterfaces (\r
1096          mCallbackInfo->DriverHandle,\r
1097          &gEfiDevicePathProtocolGuid,\r
1098          &mIScsiHiiVendorDevicePath,\r
1099          &gEfiHiiConfigAccessProtocolGuid,\r
1100          &mCallbackInfo->ConfigAccess,\r
1101          NULL\r
1102          );\r
1103   FreePool (mCallbackInfo);\r
1104 \r
1105   return EFI_SUCCESS;\r
1106 }\r