/** @file HII Config Access protocol implementation of VLAN configuration module. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "VlanConfigImpl.h" CHAR16 mVlanStorageName[] = L"VlanNvData"; EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting = NULL; VLAN_CONFIG_PRIVATE_DATA mVlanConfigPrivateDateTemplate = { VLAN_CONFIG_PRIVATE_DATA_SIGNATURE, { VlanExtractConfig, VlanRouteConfig, VlanCallback } }; VENDOR_DEVICE_PATH mHiiVendorDevicePathNode = { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { (UINT8) (sizeof (VENDOR_DEVICE_PATH)), (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) } }, VLAN_CONFIG_FORM_SET_GUID }; /** This function allows a caller to extract the current configuration for one or more named elements from the target driver. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Request A null-terminated Unicode string in format. @param[out] Progress On return, points to a character in the Request string. Points to the string's null terminator if request was successful. Points to the most recent '&' before the first failing name/value pair (or the beginning of the string if the failure is in the first name/value pair) if the request was not successful. @param[out] Results A null-terminated Unicode string in format which has all values filled in for the names in the Request string. String to be allocated by the called function. @retval EFI_SUCCESS The Results is filled with the requested values. @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. **/ EFI_STATUS EFIAPI VlanExtractConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Request, OUT EFI_STRING *Progress, OUT EFI_STRING *Results ) { EFI_STATUS Status; UINTN BufferSize; VLAN_CONFIGURATION Configuration; VLAN_CONFIG_PRIVATE_DATA *PrivateData; EFI_STRING ConfigRequestHdr; EFI_STRING ConfigRequest; BOOLEAN AllocatedRequest; UINTN Size; if (Progress == NULL || Results == NULL) { return EFI_INVALID_PARAMETER; } *Progress = Request; if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gVlanConfigFormSetGuid, mVlanStorageName)) { return EFI_NOT_FOUND; } ConfigRequestHdr = NULL; ConfigRequest = NULL; AllocatedRequest = FALSE; Size = 0; // // Retrieve the pointer to the UEFI HII Config Routing Protocol // if (mHiiConfigRouting == NULL) { gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **) &mHiiConfigRouting); } ASSERT (mHiiConfigRouting != NULL); // // Convert buffer data to by helper function BlockToConfig() // PrivateData = VLAN_CONFIG_PRIVATE_DATA_FROM_THIS (This); ZeroMem (&Configuration, sizeof (VLAN_CONFIGURATION)); BufferSize = sizeof (Configuration); ConfigRequest = Request; if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { // // Request has no request element, construct full request string. // Allocate and fill a buffer large enough to hold the template // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator // ConfigRequestHdr = HiiConstructConfigHdr (&gVlanConfigFormSetGuid, mVlanStorageName, PrivateData->DriverHandle); Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); ConfigRequest = AllocateZeroPool (Size); ASSERT (ConfigRequest != NULL); AllocatedRequest = TRUE; UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); FreePool (ConfigRequestHdr); } Status = mHiiConfigRouting->BlockToConfig ( mHiiConfigRouting, ConfigRequest, (UINT8 *) &Configuration, BufferSize, Results, Progress ); // // Free the allocated config request string. // if (AllocatedRequest) { FreePool (ConfigRequest); ConfigRequest = NULL; } // // Set Progress string to the original request string. // if (Request == NULL) { *Progress = NULL; } else if (StrStr (Request, L"OFFSET") == NULL) { *Progress = Request + StrLen (Request); } return Status; } /** This function processes the results of changes in configuration. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Configuration A null-terminated Unicode string in format. @param[out] Progress A pointer to a string filled in with the offset of the most recent '&' before the first failing name/value pair (or the beginning of the string if the failure is in the first name/value pair) or the terminating NULL if all was successful. @retval EFI_SUCCESS The Results is processed successfully. @retval EFI_INVALID_PARAMETER Configuration is NULL. @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. **/ EFI_STATUS EFIAPI VlanRouteConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Configuration, OUT EFI_STRING *Progress ) { if (Configuration == NULL || Progress == NULL) { return EFI_INVALID_PARAMETER; } *Progress = Configuration; if (!HiiIsConfigHdrMatch (Configuration, &gVlanConfigFormSetGuid, mVlanStorageName)) { return EFI_NOT_FOUND; } *Progress = Configuration + StrLen (Configuration); return EFI_SUCCESS; } /** This function processes the results of changes in configuration. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Action Specifies the type of action taken by the browser. @param[in] QuestionId A unique value which is sent to the original exporting driver so that it can identify the type of data to expect. @param[in] Type The type of value for the question. @param[in] Value A pointer to the data being sent to the original exporting driver. @param[out] ActionRequest On return, points to the action requested by the callback function. @retval EFI_SUCCESS The callback successfully handled the action. @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. @retval EFI_DEVICE_ERROR The variable could not be saved. @retval EFI_UNSUPPORTED The specified Action is not supported by the callback. **/ EFI_STATUS EFIAPI VlanCallback ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN EFI_BROWSER_ACTION Action, IN EFI_QUESTION_ID QuestionId, IN UINT8 Type, IN EFI_IFR_TYPE_VALUE *Value, OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest ) { VLAN_CONFIG_PRIVATE_DATA *PrivateData; VLAN_CONFIGURATION *Configuration; EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; UINTN Index; EFI_HANDLE VlanHandle; PrivateData = VLAN_CONFIG_PRIVATE_DATA_FROM_THIS (This); if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)) { return EFI_SUCCESS; } if ((Action != EFI_BROWSER_ACTION_CHANGED) && (Action != EFI_BROWSER_ACTION_CHANGING)) { // // All other action return unsupported. // return EFI_UNSUPPORTED; } // // Get Browser data // Configuration = AllocateZeroPool (sizeof (VLAN_CONFIGURATION)); ASSERT (Configuration != NULL); HiiGetBrowserData (&gVlanConfigFormSetGuid, mVlanStorageName, sizeof (VLAN_CONFIGURATION), (UINT8 *) Configuration); VlanConfig = PrivateData->VlanConfig; if (Action == EFI_BROWSER_ACTION_CHANGED) { switch (QuestionId) { case VLAN_ADD_QUESTION_ID: // // Add a VLAN // VlanConfig->Set (VlanConfig, Configuration->VlanId, Configuration->Priority); VlanUpdateForm (PrivateData); // // Connect the newly created VLAN device // VlanHandle = NetLibGetVlanHandle (PrivateData->ControllerHandle, Configuration->VlanId); if (VlanHandle == NULL) { // // There may be no child handle created for VLAN ID 0, connect the parent handle // VlanHandle = PrivateData->ControllerHandle; } gBS->ConnectController (VlanHandle, NULL, NULL, TRUE); // // Clear UI data // *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; Configuration->VlanId = 0; Configuration->Priority = 0; break; case VLAN_REMOVE_QUESTION_ID: // // Remove VLAN // ASSERT (PrivateData->NumberOfVlan <= MAX_VLAN_NUMBER); for (Index = 0; Index < PrivateData->NumberOfVlan; Index++) { if (Configuration->VlanList[Index] != 0) { // // Checkbox is selected, need remove this VLAN // VlanConfig->Remove (VlanConfig, PrivateData->VlanId[Index]); } } VlanUpdateForm (PrivateData); if (PrivateData->NumberOfVlan == 0) { // // No VLAN device now, connect the physical NIC handle. // Note: PrivateData->NumberOfVlan has been updated by VlanUpdateForm() // gBS->ConnectController (PrivateData->ControllerHandle, NULL, NULL, TRUE); } *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; ZeroMem (Configuration->VlanList, MAX_VLAN_NUMBER); break; default: break; } } else if (Action == EFI_BROWSER_ACTION_CHANGING) { switch (QuestionId) { case VLAN_UPDATE_QUESTION_ID: // // Update current VLAN list into Form. // VlanUpdateForm (PrivateData); break; default: break; } } HiiSetBrowserData (&gVlanConfigFormSetGuid, mVlanStorageName, sizeof (VLAN_CONFIGURATION), (UINT8 *) Configuration, NULL); FreePool (Configuration); return EFI_SUCCESS; } /** This function update VLAN list in the VLAN configuration Form. @param[in, out] PrivateData Points to VLAN configuration private data. **/ VOID VlanUpdateForm ( IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData ) { EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; UINT16 NumberOfVlan; UINTN Index; EFI_VLAN_FIND_DATA *VlanData; VOID *StartOpCodeHandle; EFI_IFR_GUID_LABEL *StartLabel; VOID *EndOpCodeHandle; EFI_IFR_GUID_LABEL *EndLabel; CHAR16 *String; CHAR16 VlanStr[30]; CHAR16 VlanIdStr[6]; UINTN DigitalCount; EFI_STRING_ID StringId; // // Find current VLAN configuration // VlanData = NULL; NumberOfVlan = 0; VlanConfig = PrivateData->VlanConfig; VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData); // // Update VLAN configuration in PrivateData // if (NumberOfVlan > MAX_VLAN_NUMBER) { NumberOfVlan = MAX_VLAN_NUMBER; } PrivateData->NumberOfVlan = NumberOfVlan; // // Init OpCode Handle // StartOpCodeHandle = HiiAllocateOpCodeHandle (); ASSERT (StartOpCodeHandle != NULL); EndOpCodeHandle = HiiAllocateOpCodeHandle (); ASSERT (EndOpCodeHandle != NULL); // // Create Hii Extend Label OpCode as the start opcode // StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL) ); StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; StartLabel->Number = LABEL_VLAN_LIST; // // Create Hii Extend Label OpCode as the end opcode // EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL) ); EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; EndLabel->Number = LABEL_END; ZeroMem (PrivateData->VlanId, MAX_VLAN_NUMBER); for (Index = 0; Index < NumberOfVlan; Index++) { String = VlanStr; StrCpyS (String, (sizeof (VlanStr) /sizeof (CHAR16)), L" VLAN ID:"); String += 10; // // Pad VlanId string up to 4 characters with space // UnicodeValueToStringS (VlanIdStr, sizeof (VlanIdStr), 0, VlanData[Index].VlanId, 5); DigitalCount = StrnLenS (VlanIdStr, ARRAY_SIZE (VlanIdStr)); SetMem16 (String, (4 - DigitalCount) * sizeof (CHAR16), L' '); StrCpyS (String + 4 - DigitalCount, (sizeof (VlanStr) /sizeof (CHAR16)) - 10 - (4 - DigitalCount), VlanIdStr); String += 4; StrCpyS (String, (sizeof (VlanStr) /sizeof (CHAR16)) - 10 - (4 - DigitalCount) - 4, L", Priority:"); String += 11; UnicodeValueToStringS ( String, sizeof (VlanStr) - ((UINTN)String - (UINTN)VlanStr), 0, VlanData[Index].Priority, 4 ); String += StrnLenS (String, ARRAY_SIZE (VlanStr) - ((UINTN)String - (UINTN)VlanStr) / sizeof (CHAR16)); *String = 0; StringId = HiiSetString (PrivateData->HiiHandle, 0, VlanStr, NULL); ASSERT (StringId != 0); HiiCreateCheckBoxOpCode ( StartOpCodeHandle, (EFI_QUESTION_ID) (VLAN_LIST_VAR_OFFSET + Index), VLAN_CONFIGURATION_VARSTORE_ID, (UINT16) (VLAN_LIST_VAR_OFFSET + Index), StringId, STRING_TOKEN (STR_VLAN_VLAN_LIST_HELP), 0, 0, NULL ); // // Save VLAN id to private data // PrivateData->VlanId[Index] = VlanData[Index].VlanId; } HiiUpdateForm ( PrivateData->HiiHandle, // HII handle &gVlanConfigFormSetGuid, // Formset GUID VLAN_CONFIGURATION_FORM_ID, // Form ID StartOpCodeHandle, // Label for where to insert opcodes EndOpCodeHandle // Replace data ); HiiFreeOpCodeHandle (StartOpCodeHandle); HiiFreeOpCodeHandle (EndOpCodeHandle); if (VlanData != NULL) { FreePool (VlanData); } } /** This function publish the VLAN configuration Form for a network device. The HII Config Access protocol will be installed on a child handle of the network device. @param[in, out] PrivateData Points to VLAN configuration private data. @retval EFI_SUCCESS HII Form is installed for this network device. @retval EFI_OUT_OF_RESOURCES Not enough resource for HII Form installation. @retval Others Other errors as indicated. **/ EFI_STATUS InstallVlanConfigForm ( IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData ) { EFI_STATUS Status; EFI_HII_HANDLE HiiHandle; EFI_HANDLE DriverHandle; CHAR16 Str[26 + sizeof (EFI_MAC_ADDRESS) * 2 + 1]; CHAR16 *MacString; EFI_DEVICE_PATH_PROTOCOL *ChildDevicePath; EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; // // Create child handle and install HII Config Access Protocol // ChildDevicePath = AppendDevicePathNode ( PrivateData->ParentDevicePath, (CONST EFI_DEVICE_PATH_PROTOCOL *) &mHiiVendorDevicePathNode ); if (ChildDevicePath == NULL) { return EFI_OUT_OF_RESOURCES; } PrivateData->ChildDevicePath = ChildDevicePath; DriverHandle = NULL; ConfigAccess = &PrivateData->ConfigAccess; Status = gBS->InstallMultipleProtocolInterfaces ( &DriverHandle, &gEfiDevicePathProtocolGuid, ChildDevicePath, &gEfiHiiConfigAccessProtocolGuid, ConfigAccess, NULL ); if (EFI_ERROR (Status)) { return Status; } PrivateData->DriverHandle = DriverHandle; // // Establish the parent-child relationship between the new created // child handle and the ControllerHandle. // Status = gBS->OpenProtocol ( PrivateData->ControllerHandle, &gEfiVlanConfigProtocolGuid, (VOID **)&VlanConfig, PrivateData->ImageHandle, PrivateData->DriverHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); if (EFI_ERROR (Status)) { return Status; } // // Publish the HII package list // HiiHandle = HiiAddPackages ( &gVlanConfigFormSetGuid, DriverHandle, VlanConfigDxeStrings, VlanConfigBin, NULL ); if (HiiHandle == NULL) { return EFI_OUT_OF_RESOURCES; } PrivateData->HiiHandle = HiiHandle; // // Update formset title help string. // MacString = NULL; Status = NetLibGetMacString (PrivateData->ControllerHandle, PrivateData->ImageHandle, &MacString); if (EFI_ERROR (Status)) { return Status; } PrivateData->MacString = MacString; StrCpyS (Str, sizeof (Str) / sizeof (CHAR16), L"VLAN Configuration (MAC:"); StrCatS (Str, sizeof (Str) / sizeof (CHAR16), MacString); StrCatS (Str, sizeof (Str) / sizeof (CHAR16), L")"); HiiSetString ( HiiHandle, STRING_TOKEN (STR_VLAN_FORM_SET_TITLE_HELP), Str, NULL ); // // Update form title help string. // HiiSetString ( HiiHandle, STRING_TOKEN (STR_VLAN_FORM_HELP), Str, NULL ); return EFI_SUCCESS; } /** This function remove the VLAN configuration Form for a network device. The child handle for HII Config Access protocol will be destroyed. @param[in, out] PrivateData Points to VLAN configuration private data. @retval EFI_SUCCESS HII Form has been uninstalled successfully. @retval Others Other errors as indicated. **/ EFI_STATUS UninstallVlanConfigForm ( IN OUT VLAN_CONFIG_PRIVATE_DATA *PrivateData ) { EFI_STATUS Status; EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; // // End the parent-child relationship. // Status = gBS->CloseProtocol ( PrivateData->ControllerHandle, &gEfiVlanConfigProtocolGuid, PrivateData->ImageHandle, PrivateData->DriverHandle ); if (EFI_ERROR (Status)) { return Status; } // // Uninstall HII Config Access Protocol // if (PrivateData->DriverHandle != NULL) { Status = gBS->UninstallMultipleProtocolInterfaces ( PrivateData->DriverHandle, &gEfiDevicePathProtocolGuid, PrivateData->ChildDevicePath, &gEfiHiiConfigAccessProtocolGuid, &PrivateData->ConfigAccess, NULL ); if (EFI_ERROR (Status)) { gBS->OpenProtocol ( PrivateData->ControllerHandle, &gEfiVlanConfigProtocolGuid, (VOID **)&VlanConfig, PrivateData->ImageHandle, PrivateData->DriverHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); return Status; } PrivateData->DriverHandle = NULL; if (PrivateData->ChildDevicePath != NULL) { FreePool (PrivateData->ChildDevicePath); PrivateData->ChildDevicePath = NULL; } } // // Free MAC string // if (PrivateData->MacString != NULL) { FreePool (PrivateData->MacString); PrivateData->MacString = NULL; } // // Uninstall HII package list // if (PrivateData->HiiHandle != NULL) { HiiRemovePackages (PrivateData->HiiHandle); PrivateData->HiiHandle = NULL; } return EFI_SUCCESS; }