]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/WifiConnectionManagerDxe/WifiConnectionMgrImpl.c
NetworkPkg: Add WiFi Connection Manager to NetworkPkg
[mirror_edk2.git] / NetworkPkg / WifiConnectionManagerDxe / WifiConnectionMgrImpl.c
diff --git a/NetworkPkg/WifiConnectionManagerDxe/WifiConnectionMgrImpl.c b/NetworkPkg/WifiConnectionManagerDxe/WifiConnectionMgrImpl.c
new file mode 100644 (file)
index 0000000..52ce18d
--- /dev/null
@@ -0,0 +1,1292 @@
+/** @file\r
+  The Mac Connection2 Protocol adapter functions for WiFi Connection Manager.\r
+\r
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "WifiConnectionMgrDxe.h"\r
+\r
+EFI_EAP_TYPE mEapAuthMethod[] = {\r
+  EFI_EAP_TYPE_TTLS,\r
+  EFI_EAP_TYPE_PEAP,\r
+  EFI_EAP_TYPE_EAPTLS\r
+};\r
+\r
+EFI_EAP_TYPE mEapSecondAuthMethod[] = {\r
+  EFI_EAP_TYPE_MSCHAPV2\r
+};\r
+\r
+/**\r
+  The callback function for scan operation. This function updates networks\r
+  according to the latest scan result, and trigger UI refresh.\r
+\r
+  ASSERT when errors occur in config token.\r
+\r
+  @param[in]  Event                 The GetNetworks token receive event.\r
+  @param[in]  Context               The context of the GetNetworks token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+WifiMgrOnScanFinished (\r
+  IN  EFI_EVENT                     Event,\r
+  IN  VOID                          *Context\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  WIFI_MGR_MAC_CONFIG_TOKEN         *ConfigToken;\r
+  WIFI_MGR_DEVICE_DATA              *Nic;\r
+  WIFI_MGR_NETWORK_PROFILE          *Profile;\r
+  EFI_80211_NETWORK                 *Network;\r
+  UINTN                             DataSize;\r
+  EFI_80211_NETWORK_DESCRIPTION     *NetworkDescription;\r
+  EFI_80211_GET_NETWORKS_RESULT     *Result;\r
+  LIST_ENTRY                        *Entry;\r
+  UINT8                             SecurityType;\r
+  BOOLEAN                           AKMSuiteSupported;\r
+  BOOLEAN                           CipherSuiteSupported;\r
+  CHAR8                             *AsciiSSId;\r
+  UINTN                             Index;\r
+\r
+  ASSERT (Context != NULL);\r
+\r
+  ConfigToken = (WIFI_MGR_MAC_CONFIG_TOKEN *) Context;\r
+  ASSERT (ConfigToken->Nic != NULL);\r
+  ASSERT (ConfigToken->Type == TokenTypeGetNetworksToken);\r
+\r
+  //\r
+  // It is the GetNetworks token, set scan state to "ScanFinished"\r
+  //\r
+  ConfigToken->Nic->ScanState = WifiMgrScanFinished;\r
+\r
+  ASSERT (ConfigToken->Token.GetNetworksToken != NULL);\r
+  Result = ConfigToken->Token.GetNetworksToken->Result;\r
+  Nic    = ConfigToken->Nic;\r
+\r
+  //\r
+  // Clean previous result, and update network list according to the scan result\r
+  //\r
+  Nic->AvailableCount    = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &Nic->ProfileList) {\r
+    Profile = NET_LIST_USER_STRUCT_S (Entry, WIFI_MGR_NETWORK_PROFILE,\r
+                Link, WIFI_MGR_PROFILE_SIGNATURE);\r
+    Profile->IsAvailable = FALSE;\r
+  }\r
+\r
+  if (Result == NULL) {\r
+    gBS->SignalEvent (Nic->Private->NetworkListRefreshEvent);\r
+    WifiMgrFreeToken(ConfigToken);\r
+    return;\r
+  }\r
+\r
+  for (Index = 0; Index < Result->NumOfNetworkDesc; Index ++) {\r
+\r
+    NetworkDescription = Result->NetworkDesc + Index;\r
+    if (NetworkDescription == NULL) {\r
+      continue;\r
+    }\r
+\r
+    Network = &NetworkDescription->Network;\r
+    if (Network == NULL || Network->SSId.SSIdLen == 0) {\r
+      continue;\r
+    }\r
+\r
+    Status = WifiMgrCheckRSN (\r
+               Network->AKMSuite,\r
+               Network->CipherSuite,\r
+               Nic,\r
+               &SecurityType,\r
+               &AKMSuiteSupported,\r
+               &CipherSuiteSupported\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+\r
+      SecurityType          = SECURITY_TYPE_UNKNOWN;\r
+      AKMSuiteSupported     = FALSE;\r
+      CipherSuiteSupported  = FALSE;\r
+    }\r
+\r
+    AsciiSSId = (CHAR8*) AllocateZeroPool(sizeof (CHAR8) * (Network->SSId.SSIdLen + 1));\r
+    if (AsciiSSId == NULL) {\r
+      continue;\r
+    }\r
+    CopyMem(AsciiSSId, (CHAR8 *) Network->SSId.SSId, sizeof (CHAR8) * Network->SSId.SSIdLen);\r
+    *(AsciiSSId + Network->SSId.SSIdLen) = '\0';\r
+\r
+    Profile = WifiMgrGetProfileByAsciiSSId (AsciiSSId, SecurityType, &Nic->ProfileList);\r
+    if (Profile == NULL) {\r
+\r
+      if (Nic->MaxProfileIndex >= NETWORK_LIST_COUNT_MAX) {\r
+        FreePool (AsciiSSId);\r
+        continue;\r
+      }\r
+\r
+      //\r
+      // Create a new profile\r
+      //\r
+      Profile = AllocateZeroPool (sizeof (WIFI_MGR_NETWORK_PROFILE));\r
+      if (Profile == NULL) {\r
+        FreePool (AsciiSSId);\r
+        continue;\r
+      }\r
+      Profile->Signature    = WIFI_MGR_PROFILE_SIGNATURE;\r
+      Profile->NicIndex     = Nic->NicIndex;\r
+      Profile->ProfileIndex = Nic->MaxProfileIndex + 1;\r
+      AsciiStrToUnicodeStrS (AsciiSSId, Profile->SSId, SSID_STORAGE_SIZE);\r
+      InsertTailList (&Nic->ProfileList, &Profile->Link);\r
+      Nic->MaxProfileIndex ++;\r
+    }\r
+    FreePool (AsciiSSId);\r
+\r
+    //\r
+    //May receive duplicate networks in scan results, check if it has already\r
+    //been processed.\r
+    //\r
+    if (!Profile->IsAvailable) {\r
+\r
+      Profile->IsAvailable          = TRUE;\r
+      Profile->SecurityType         = SecurityType;\r
+      Profile->AKMSuiteSupported    = AKMSuiteSupported;\r
+      Profile->CipherSuiteSupported = CipherSuiteSupported;\r
+      Profile->NetworkQuality       = NetworkDescription->NetworkQuality;\r
+      Nic->AvailableCount ++;\r
+\r
+      //\r
+      //Copy BSSType and SSId\r
+      //\r
+      CopyMem(&Profile->Network, Network, sizeof (EFI_80211_NETWORK));\r
+\r
+      //\r
+      //Copy AKMSuite list\r
+      //\r
+      if (Network->AKMSuite != NULL) {\r
+\r
+        if (Network->AKMSuite->AKMSuiteCount == 0) {\r
+          DataSize = sizeof (EFI_80211_AKM_SUITE_SELECTOR);\r
+        } else {\r
+          DataSize = sizeof (EFI_80211_AKM_SUITE_SELECTOR) + sizeof (EFI_80211_SUITE_SELECTOR)\r
+                       * (Network->AKMSuite->AKMSuiteCount - 1);\r
+        }\r
+        Profile->Network.AKMSuite = (EFI_80211_AKM_SUITE_SELECTOR *) AllocateZeroPool (DataSize);\r
+        if (Profile->Network.AKMSuite == NULL) {\r
+          continue;\r
+        }\r
+        CopyMem (Profile->Network.AKMSuite, Network->AKMSuite, DataSize);\r
+      }\r
+\r
+      //\r
+      //Copy CipherSuite list\r
+      //\r
+      if (Network->CipherSuite != NULL) {\r
+\r
+        if (Network->CipherSuite->CipherSuiteCount == 0) {\r
+          DataSize = sizeof (EFI_80211_CIPHER_SUITE_SELECTOR);\r
+        } else {\r
+          DataSize = sizeof (EFI_80211_CIPHER_SUITE_SELECTOR) + sizeof (EFI_80211_SUITE_SELECTOR)\r
+                       * (Network->CipherSuite->CipherSuiteCount - 1);\r
+        }\r
+        Profile->Network.CipherSuite = (EFI_80211_CIPHER_SUITE_SELECTOR *) AllocateZeroPool (DataSize);\r
+        if (Profile->Network.CipherSuite == NULL) {\r
+          continue;\r
+        }\r
+        CopyMem (Profile->Network.CipherSuite, Network->CipherSuite, DataSize);\r
+      }\r
+    } else {\r
+      //\r
+      // A duplicate network, update signal quality\r
+      //\r
+      if (Profile->NetworkQuality < NetworkDescription->NetworkQuality) {\r
+        Profile->NetworkQuality = NetworkDescription->NetworkQuality;\r
+      }\r
+      continue;\r
+    }\r
+  }\r
+\r
+  FreePool (Result);\r
+  gBS->SignalEvent (Nic->Private->NetworkListRefreshEvent);\r
+\r
+  //\r
+  // The current connected network should always be available until disconnection\r
+  // happens in Wifi FW layer, even when it is not in this time's scan result.\r
+  //\r
+  if (Nic->ConnectState == WifiMgrConnectedToAp && Nic->CurrentOperateNetwork != NULL) {\r
+    if (!Nic->CurrentOperateNetwork->IsAvailable) {\r
+      Nic->CurrentOperateNetwork->IsAvailable = TRUE;\r
+      Nic->AvailableCount ++;\r
+    }\r
+  }\r
+\r
+  WifiMgrFreeToken(ConfigToken);\r
+}\r
+\r
+/**\r
+  Start scan operation, and send out a token to collect available networks.\r
+\r
+  @param[in]  Nic                 Pointer to the device data of the selected NIC.\r
+\r
+  @retval EFI_SUCCESS             The operation is completed.\r
+  @retval EFI_ALREADY_STARTED     A former scan operation is already ongoing.\r
+  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+  @retval Other Errors            Return errors when getting networks from low layer.\r
+\r
+**/\r
+EFI_STATUS\r
+WifiMgrStartScan (\r
+  IN      WIFI_MGR_DEVICE_DATA        *Nic\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  EFI_TPL                             OldTpl;\r
+  WIFI_MGR_MAC_CONFIG_TOKEN           *ConfigToken;\r
+  EFI_80211_GET_NETWORKS_TOKEN        *GetNetworksToken;\r
+  UINT32                              HiddenSSIdIndex;\r
+  UINT32                              HiddenSSIdCount;\r
+  EFI_80211_SSID                      *HiddenSSIdList;\r
+  WIFI_HIDDEN_NETWORK_DATA            *HiddenNetwork;\r
+  LIST_ENTRY                          *Entry;\r
+\r
+  if (Nic == NULL || Nic->Wmp == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Nic->ScanState == WifiMgrScanning) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Nic->ScanState  = WifiMgrScanning;\r
+  OldTpl          = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Status          = EFI_SUCCESS;\r
+  HiddenSSIdList  = NULL;\r
+  HiddenSSIdCount = Nic->Private->HiddenNetworkCount;\r
+  HiddenSSIdIndex = 0;\r
+\r
+  //\r
+  //create a new get network token\r
+  //\r
+  ConfigToken     = AllocateZeroPool (sizeof (WIFI_MGR_MAC_CONFIG_TOKEN));\r
+  if (ConfigToken == NULL) {\r
+    gBS->RestoreTPL (OldTpl);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ConfigToken->Type      = TokenTypeGetNetworksToken;\r
+  ConfigToken->Nic       = Nic;\r
+  ConfigToken->Token.GetNetworksToken = AllocateZeroPool (sizeof (EFI_80211_GET_NETWORKS_TOKEN));\r
+  if (ConfigToken->Token.GetNetworksToken == NULL) {\r
+    WifiMgrFreeToken(ConfigToken);\r
+    gBS->RestoreTPL (OldTpl);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  GetNetworksToken = ConfigToken->Token.GetNetworksToken;\r
+\r
+  //\r
+  // There are some hidden networks to scan, add them into scan list\r
+  //\r
+  if (HiddenSSIdCount > 0) {\r
+    HiddenSSIdList = AllocateZeroPool(HiddenSSIdCount * sizeof (EFI_80211_SSID));\r
+    if (HiddenSSIdList == NULL) {\r
+      WifiMgrFreeToken(ConfigToken);\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    HiddenSSIdIndex = 0;\r
+    NET_LIST_FOR_EACH (Entry, &Nic->Private->HiddenNetworkList) {\r
+\r
+      HiddenNetwork = NET_LIST_USER_STRUCT_S (Entry, WIFI_HIDDEN_NETWORK_DATA,\r
+                        Link, WIFI_MGR_HIDDEN_NETWORK_SIGNATURE);\r
+      HiddenSSIdList[HiddenSSIdIndex].SSIdLen = (UINT8) StrLen (HiddenNetwork->SSId);\r
+      UnicodeStrToAsciiStrS(HiddenNetwork->SSId,\r
+        (CHAR8 *) HiddenSSIdList[HiddenSSIdIndex].SSId, SSID_STORAGE_SIZE);\r
+      HiddenSSIdIndex ++;\r
+    }\r
+    GetNetworksToken->Data = AllocateZeroPool (sizeof (EFI_80211_GET_NETWORKS_DATA) +\r
+                               (HiddenSSIdCount - 1) * sizeof (EFI_80211_SSID));\r
+    if (GetNetworksToken->Data == NULL) {\r
+      FreePool (HiddenSSIdList);\r
+      WifiMgrFreeToken(ConfigToken);\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    GetNetworksToken->Data->NumOfSSID = HiddenSSIdCount;\r
+    CopyMem(GetNetworksToken->Data->SSIDList, HiddenSSIdList, HiddenSSIdCount * sizeof (EFI_80211_SSID));\r
+    FreePool(HiddenSSIdList);\r
+  } else {\r
+\r
+    GetNetworksToken->Data = AllocateZeroPool (sizeof (EFI_80211_GET_NETWORKS_DATA));\r
+    if (GetNetworksToken->Data == NULL) {\r
+      WifiMgrFreeToken(ConfigToken);\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    GetNetworksToken->Data->NumOfSSID = 0;\r
+  }\r
+\r
+  //\r
+  //Create a handle when scan process ends\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  WifiMgrOnScanFinished,\r
+                  ConfigToken,\r
+                  &GetNetworksToken->Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    WifiMgrFreeToken(ConfigToken);\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  //Start scan ...\r
+  //\r
+  Status = Nic->Wmp->GetNetworks (Nic->Wmp, GetNetworksToken);\r
+  if (EFI_ERROR (Status)) {\r
+    Nic->ScanState  = WifiMgrScanFinished;\r
+    WifiMgrFreeToken(ConfigToken);\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Configure password to supplicant before connecting to a secured network.\r
+\r
+  @param[in]  Nic                 Pointer to the device data of the selected NIC.\r
+  @param[in]  Profile             The target network to be connected.\r
+\r
+  @retval EFI_SUCCESS             The operation is completed.\r
+  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+  @retval EFI_NOT_FOUND           No valid password is found to configure.\r
+  @retval Other Errors            Returned errors when setting data to supplicant.\r
+\r
+**/\r
+EFI_STATUS\r
+WifiMgrConfigPassword (\r
+  IN    WIFI_MGR_DEVICE_DATA              *Nic,\r
+  IN    WIFI_MGR_NETWORK_PROFILE          *Profile\r
+  )\r
+{\r
+  EFI_STATUS                 Status;\r
+  EFI_SUPPLICANT_PROTOCOL    *Supplicant;\r
+  EFI_80211_SSID             SSId;\r
+  UINT8                      *AsciiPassword;\r
+\r
+  if (Nic == NULL || Nic->Supplicant == NULL || Profile == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  Supplicant = Nic->Supplicant;\r
+  //\r
+  //Set SSId to supplicant\r
+  //\r
+  SSId.SSIdLen = Profile->Network.SSId.SSIdLen;\r
+  CopyMem(SSId.SSId, Profile->Network.SSId.SSId, sizeof (Profile->Network.SSId.SSId));\r
+  Status = Supplicant->SetData(Supplicant,EfiSupplicant80211TargetSSIDName,\r
+                         (VOID *)&SSId, sizeof(EFI_80211_SSID));\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  //Set password to supplicant\r
+  //\r
+  if (StrLen (Profile->Password) < PASSWORD_MIN_LEN) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  AsciiPassword = AllocateZeroPool ((StrLen(Profile->Password) + 1) * sizeof (UINT8));\r
+  if (AsciiPassword == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  UnicodeStrToAsciiStrS (Profile->Password, (CHAR8 *) AsciiPassword, PASSWORD_STORAGE_SIZE);\r
+  Status = Supplicant->SetData (Supplicant, EfiSupplicant80211PskPassword,\r
+                         AsciiPassword, (StrLen(Profile->Password) + 1) * sizeof (UINT8));\r
+  ZeroMem (AsciiPassword, AsciiStrLen ((CHAR8 *) AsciiPassword) + 1);\r
+  FreePool(AsciiPassword);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Conduct EAP configuration to supplicant before connecting to a EAP network.\r
+  Current WiFi Connection Manager only supports three kinds of EAP networks:\r
+  1). EAP-TLS (Two-Way Authentication is required in our implementation)\r
+  2). EAP-TTLS/MSCHAPv2 (One-Way Authentication is required in our implementation)\r
+  3). PEAPv0/MSCHAPv2 (One-Way Authentication is required in our implementation)\r
+\r
+  @param[in]  Nic                 Pointer to the device data of the selected NIC.\r
+  @param[in]  Profile             The target network to be connected.\r
+\r
+  @retval EFI_SUCCESS             The operation is completed.\r
+  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.\r
+  @retval EFI_UNSUPPORTED         The expected EAP method is not supported.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+  @retval Other Errors            Returned errors when setting data to supplicant.\r
+\r
+**/\r
+EFI_STATUS\r
+WifiMgrConfigEap (\r
+  IN    WIFI_MGR_DEVICE_DATA              *Nic,\r
+  IN    WIFI_MGR_NETWORK_PROFILE          *Profile\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_EAP_CONFIGURATION_PROTOCOL    *EapConfig;\r
+  EFI_EAP_TYPE                      EapAuthMethod;\r
+  EFI_EAP_TYPE                      EapSecondAuthMethod;\r
+  EFI_EAP_TYPE                      *AuthMethodList;\r
+  CHAR8                             *Identity;\r
+  UINTN                             IdentitySize;\r
+  CHAR16                            *Password;\r
+  UINTN                             PasswordSize;\r
+  UINTN                             EncryptPasswordLen;\r
+  CHAR8                             *AsciiEncryptPassword;\r
+  UINTN                             AuthMethodListSize;\r
+  UINTN                             Index;\r
+\r
+  if (Nic == NULL || Nic->EapConfig == NULL || Profile == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  EapConfig = Nic->EapConfig;\r
+\r
+  if (Profile->EapAuthMethod >= EAP_AUTH_METHOD_MAX) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  EapAuthMethod = mEapAuthMethod[Profile->EapAuthMethod];\r
+\r
+  if (EapAuthMethod != EFI_EAP_TYPE_EAPTLS) {\r
+    if (Profile->EapSecondAuthMethod >= EAP_SEAUTH_METHOD_MAX) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    EapSecondAuthMethod = mEapSecondAuthMethod[Profile->EapSecondAuthMethod];\r
+  }\r
+\r
+  //\r
+  //The first time to get Supported Auth Method list, return the size.\r
+  //\r
+  AuthMethodListSize  = 0;\r
+  AuthMethodList      = NULL;\r
+  Status = EapConfig->GetData (EapConfig, EFI_EAP_TYPE_ATTRIBUTE, EfiEapConfigEapSupportedAuthMethod,\r
+                        (VOID *) AuthMethodList, &AuthMethodListSize);\r
+  if (Status == EFI_SUCCESS) {\r
+    //\r
+    //No Supported Eap Auth Method\r
+    //\r
+    return EFI_UNSUPPORTED;\r
+  } else if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // The second time to get Supported Auth Method list, return the list.\r
+  // In current design, only EAPTLS, TTLS and PEAP are supported\r
+  //\r
+  AuthMethodList = (EFI_EAP_TYPE *) AllocateZeroPool(AuthMethodListSize);\r
+  if (AuthMethodList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  Status = EapConfig->GetData (EapConfig, EFI_EAP_TYPE_ATTRIBUTE, EfiEapConfigEapSupportedAuthMethod,\r
+                        (VOID *) AuthMethodList, &AuthMethodListSize);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (AuthMethodList);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  //Check if EapAuthMethod is in supported Auth Method list, if found, skip the loop.\r
+  //\r
+  for (Index = 0; Index < AuthMethodListSize / sizeof (EFI_EAP_TYPE); Index ++) {\r
+    if (EapAuthMethod == AuthMethodList[Index]) {\r
+      break;\r
+    }\r
+  }\r
+  if (Index == AuthMethodListSize / sizeof (EFI_EAP_TYPE)) {\r
+    FreePool (AuthMethodList);\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  FreePool (AuthMethodList);\r
+\r
+  //\r
+  // Set Identity to Eap peer, Mandatory field for PEAP and TTLS\r
+  //\r
+  if (StrLen (Profile->EapIdentity) > 0) {\r
+\r
+    IdentitySize = sizeof(CHAR8) * (StrLen(Profile->EapIdentity) + 1);\r
+    Identity = AllocateZeroPool (IdentitySize);\r
+    if (Identity == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    UnicodeStrToAsciiStrS(Profile->EapIdentity, Identity, IdentitySize);\r
+    Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_IDENTITY, EfiEapConfigIdentityString,\r
+                          (VOID *) Identity, IdentitySize - 1);\r
+    if (EFI_ERROR(Status)) {\r
+      FreePool (Identity);\r
+      return Status;\r
+    }\r
+    FreePool (Identity);\r
+  } else {\r
+    if (EapAuthMethod != EFI_EAP_TYPE_EAPTLS) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  //\r
+  //Set Auth Method to Eap peer, Mandatory field\r
+  //\r
+  Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_ATTRIBUTE, EfiEapConfigEapAuthMethod,\r
+                        (VOID *) &EapAuthMethod, sizeof (EapAuthMethod));\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (EapAuthMethod == EFI_EAP_TYPE_TTLS || EapAuthMethod == EFI_EAP_TYPE_PEAP) {\r
+\r
+    Status = EapConfig->SetData (EapConfig, EapAuthMethod, EfiEapConfigEap2ndAuthMethod,\r
+                          (VOID *) &EapSecondAuthMethod, sizeof (EapSecondAuthMethod));\r
+    if (EFI_ERROR(Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Set Password to Eap peer\r
+    //\r
+    if (StrLen (Profile->EapPassword) < PASSWORD_MIN_LEN) {\r
+\r
+      DEBUG ((DEBUG_ERROR, "[WiFi Connection Manager] Error: No Eap Password for Network: %s.\n", Profile->SSId));\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    PasswordSize = sizeof (CHAR16) * (StrLen (Profile->EapPassword) + 1);\r
+    Password = AllocateZeroPool (PasswordSize);\r
+    if (Password == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    StrCpyS (Password, PasswordSize, Profile->EapPassword);;\r
+    Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_MSCHAPV2, EfiEapConfigEapMSChapV2Password,\r
+               (VOID *) Password, PasswordSize);\r
+    ZeroMem (Password, PasswordSize);\r
+    FreePool (Password);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    //If CA cert is required, set it to Eap peer\r
+    //\r
+    if (Profile->CACertData != NULL) {\r
+\r
+      Status = EapConfig->SetData (EapConfig, EapAuthMethod, EfiEapConfigEapTlsCACert,\r
+                 Profile->CACertData, Profile->CACertSize);\r
+      if (EFI_ERROR(Status)) {\r
+        return Status;\r
+      }\r
+    } else {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  } else if (EapAuthMethod == EFI_EAP_TYPE_EAPTLS) {\r
+\r
+    //\r
+    //Set CA cert to Eap peer\r
+    //\r
+    if (Profile->CACertData == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_EAPTLS, EfiEapConfigEapTlsCACert,\r
+               Profile->CACertData, Profile->CACertSize);\r
+    if (EFI_ERROR(Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    //Set Client cert to Eap peer\r
+    //\r
+    if (Profile->ClientCertData == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_EAPTLS, EfiEapConfigEapTlsClientCert,\r
+               Profile->ClientCertData, Profile->ClientCertSize);\r
+    if (EFI_ERROR(Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    //Set Private key to Eap peer\r
+    //\r
+    if (Profile->PrivateKeyData == NULL) {\r
+\r
+      DEBUG ((DEBUG_ERROR, "[WiFi Connection Manager]  Error: No Private Key for Network: %s.\n", Profile->SSId));\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Status = EapConfig->SetData (EapConfig, EFI_EAP_TYPE_EAPTLS, EfiEapConfigEapTlsClientPrivateKeyFile,\r
+               Profile->PrivateKeyData, Profile->PrivateKeyDataSize);\r
+    if (EFI_ERROR(Status)) {\r
+      return Status;\r
+    }\r
+\r
+    if (StrLen (Profile->PrivateKeyPassword) > 0) {\r
+\r
+      EncryptPasswordLen = StrLen (Profile->PrivateKeyPassword);\r
+      AsciiEncryptPassword = AllocateZeroPool(EncryptPasswordLen + 1);\r
+      if (AsciiEncryptPassword == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      UnicodeStrToAsciiStrS(Profile->PrivateKeyPassword, AsciiEncryptPassword, EncryptPasswordLen + 1);\r
+      Status = EapConfig->SetData(EapConfig, EFI_EAP_TYPE_EAPTLS,\r
+                                    EfiEapConfigEapTlsClientPrivateKeyFilePassword,\r
+                                    (VOID *) AsciiEncryptPassword, EncryptPasswordLen + 1);\r
+      if (EFI_ERROR(Status)) {\r
+\r
+        ZeroMem (AsciiEncryptPassword, EncryptPasswordLen + 1);\r
+        FreePool (AsciiEncryptPassword);\r
+        return Status;\r
+      }\r
+\r
+      ZeroMem (AsciiEncryptPassword, EncryptPasswordLen + 1);\r
+      FreePool (AsciiEncryptPassword);\r
+    }\r
+  } else {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get current link state from low layer.\r
+\r
+  @param[in]   Nic                Pointer to the device data of the selected NIC.\r
+  @param[out]  LinkState          The pointer to buffer to retrieve link state.\r
+\r
+  @retval EFI_SUCCESS             The operation is completed.\r
+  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.\r
+  @retval EFI_UNSUPPORTED         Adapter information protocol is not supported.\r
+  @retval Other Errors            Returned errors when retrieving link state from low layer.\r
+\r
+**/\r
+EFI_STATUS\r
+WifiMgrGetLinkState (\r
+  IN   WIFI_MGR_DEVICE_DATA            *Nic,\r
+  OUT  EFI_ADAPTER_INFO_MEDIA_STATE    *LinkState\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_TPL                              OldTpl;\r
+  UINTN                                DataSize;\r
+  EFI_ADAPTER_INFO_MEDIA_STATE         *UndiState;\r
+  EFI_ADAPTER_INFORMATION_PROTOCOL     *Aip;\r
+\r
+  if (Nic == NULL || LinkState == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Status = gBS->OpenProtocol (\r
+                  Nic->ControllerHandle,\r
+                  &gEfiAdapterInformationProtocolGuid,\r
+                  (VOID**) &Aip,\r
+                  Nic->DriverHandle,\r
+                  Nic->ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->RestoreTPL (OldTpl);\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = Aip->GetInformation(\r
+                  Aip,\r
+                  &gEfiAdapterInfoMediaStateGuid,\r
+                  (VOID **) &UndiState,\r
+                  &DataSize\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  CopyMem (LinkState, UndiState, sizeof (EFI_ADAPTER_INFO_MEDIA_STATE));\r
+  FreePool (UndiState);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Prepare configuration work before connecting to the target network.\r
+  For WPA2 Personal networks, password should be checked; and for EAP networks, parameters\r
+  are different for different networks.\r
+\r
+  @param[in]  Nic                 Pointer to the device data of the selected NIC.\r
+  @param[in]  Profile             The target network to be connected.\r
+\r
+  @retval EFI_SUCCESS             The operation is completed.\r
+  @retval EFI_UNSUPPORTED         This network is not supported.\r
+  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+WifiMgrPrepareConnection (\r
+  IN    WIFI_MGR_DEVICE_DATA              *Nic,\r
+  IN    WIFI_MGR_NETWORK_PROFILE          *Profile\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  UINT8                SecurityType;\r
+  BOOLEAN              AKMSuiteSupported;\r
+  BOOLEAN              CipherSuiteSupported;\r
+\r
+  if (Profile == NULL || Nic == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = WifiMgrCheckRSN (Profile->Network.AKMSuite, Profile->Network.CipherSuite,\r
+             Nic, &SecurityType, &AKMSuiteSupported, &CipherSuiteSupported);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (AKMSuiteSupported && CipherSuiteSupported) {\r
+    switch (SecurityType) {\r
+      case SECURITY_TYPE_WPA2_PERSONAL:\r
+\r
+        Status = WifiMgrConfigPassword (Nic, Profile);\r
+        if (EFI_ERROR (Status)) {\r
+          if (Status == EFI_NOT_FOUND) {\r
+            if (Nic->OneTimeConnectRequest) {\r
+              WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Invalid Password!");\r
+            }\r
+          }\r
+          return Status;\r
+        }\r
+        break;\r
+\r
+      case SECURITY_TYPE_WPA2_ENTERPRISE:\r
+\r
+        Status = WifiMgrConfigEap (Nic, Profile);\r
+        if (EFI_ERROR (Status)) {\r
+          if (Status == EFI_INVALID_PARAMETER) {\r
+            if (Nic->OneTimeConnectRequest) {\r
+              WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Invalid Configuration!");\r
+            }\r
+          }\r
+          return Status;\r
+        }\r
+        break;\r
+\r
+      case SECURITY_TYPE_NONE:\r
+        break;\r
+\r
+      default:\r
+        return EFI_UNSUPPORTED;\r
+    }\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  The callback function for connect operation.\r
+\r
+  ASSERT when errors occur in config token.\r
+\r
+  @param[in]  Event                 The Connect token receive event.\r
+  @param[in]  Context               The context of the connect token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+WifiMgrOnConnectFinished (\r
+  IN  EFI_EVENT              Event,\r
+  IN  VOID                   *Context\r
+  )\r
+{\r
+  EFI_STATUS                         Status;\r
+  WIFI_MGR_MAC_CONFIG_TOKEN          *ConfigToken;\r
+  WIFI_MGR_NETWORK_PROFILE           *ConnectedProfile;\r
+  UINT8                              SecurityType;\r
+  UINT8                              SSIdLen;\r
+  CHAR8                              *AsciiSSId;\r
+\r
+  ASSERT (Context != NULL);\r
+\r
+  ConnectedProfile = NULL;\r
+  ConfigToken = (WIFI_MGR_MAC_CONFIG_TOKEN*) Context;\r
+  ASSERT (ConfigToken->Nic != NULL);\r
+\r
+  ConfigToken->Nic->ConnectState = WifiMgrDisconnected;\r
+  ASSERT (ConfigToken->Type == TokenTypeConnectNetworkToken);\r
+\r
+  ASSERT (ConfigToken->Token.ConnectNetworkToken != NULL);\r
+  if (ConfigToken->Token.ConnectNetworkToken->Status != EFI_SUCCESS) {\r
+\r
+    if (ConfigToken->Nic->OneTimeConnectRequest) {\r
+      //\r
+      // Only update message for user triggered connection\r
+      //\r
+      if (ConfigToken->Token.ConnectNetworkToken->Status == EFI_ACCESS_DENIED) {\r
+\r
+        WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed: Permission Denied!");\r
+      } else {\r
+        WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed!");\r
+      }\r
+      ConfigToken->Nic->OneTimeConnectRequest = FALSE;\r
+    }\r
+    ConfigToken->Nic->CurrentOperateNetwork = NULL;\r
+    return;\r
+  }\r
+\r
+  if (ConfigToken->Token.ConnectNetworkToken->ResultCode != ConnectSuccess) {\r
+\r
+    if (ConfigToken->Nic->OneTimeConnectRequest) {\r
+\r
+      if (ConfigToken->Token.ConnectNetworkToken->ResultCode == ConnectFailedReasonUnspecified) {\r
+        WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed: Wrong Password or Unexpected Error!");\r
+      } else {\r
+        WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Connect Failed!");\r
+      }\r
+    }\r
+    goto Exit;\r
+  }\r
+\r
+  if (ConfigToken->Token.ConnectNetworkToken->Data == NULL ||\r
+    ConfigToken->Token.ConnectNetworkToken->Data->Network == NULL) {\r
+\r
+    //\r
+    // An unexpected error occurs, tell low layer to perform a disconnect\r
+    //\r
+    ConfigToken->Nic->HasDisconnectPendingNetwork = TRUE;\r
+    WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // A correct connect token received, terminate the connection process\r
+  //\r
+  Status = WifiMgrCheckRSN(ConfigToken->Token.ConnectNetworkToken->Data->Network->AKMSuite,\r
+             ConfigToken->Token.ConnectNetworkToken->Data->Network->CipherSuite,\r
+             ConfigToken->Nic, &SecurityType, NULL, NULL);\r
+  if (EFI_ERROR(Status)) {\r
+    SecurityType = SECURITY_TYPE_UNKNOWN;\r
+  }\r
+\r
+  SSIdLen   = ConfigToken->Token.ConnectNetworkToken->Data->Network->SSId.SSIdLen;\r
+  AsciiSSId = (CHAR8*) AllocateZeroPool(sizeof (CHAR8) * (SSIdLen + 1));\r
+  if (AsciiSSId == NULL) {\r
+    ConfigToken->Nic->HasDisconnectPendingNetwork = TRUE;\r
+    WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);\r
+    goto Exit;\r
+  }\r
+\r
+  CopyMem(AsciiSSId, ConfigToken->Token.ConnectNetworkToken->Data->Network->SSId.SSId, SSIdLen);\r
+  *(AsciiSSId + SSIdLen) = '\0';\r
+\r
+  ConnectedProfile = WifiMgrGetProfileByAsciiSSId(AsciiSSId, SecurityType, &ConfigToken->Nic->ProfileList);\r
+  FreePool(AsciiSSId);\r
+  if (ConnectedProfile == NULL) {\r
+    ConfigToken->Nic->HasDisconnectPendingNetwork = TRUE;\r
+    WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);\r
+    goto Exit;\r
+  }\r
+\r
+  ConfigToken->Nic->ConnectState = WifiMgrConnectedToAp;\r
+  WifiMgrUpdateConnectMessage (ConfigToken->Nic, TRUE, NULL);\r
+\r
+Exit:\r
+\r
+  if (ConfigToken->Nic->ConnectState == WifiMgrDisconnected) {\r
+    ConfigToken->Nic->CurrentOperateNetwork = NULL;\r
+  }\r
+  ConfigToken->Nic->OneTimeConnectRequest = FALSE;\r
+  WifiMgrFreeToken(ConfigToken);\r
+}\r
+\r
+/**\r
+  Start connect operation, and send out a token to connect to a target network.\r
+\r
+  @param[in]  Nic                 Pointer to the device data of the selected NIC.\r
+  @param[in]  Profile             The target network to be connected.\r
+\r
+  @retval EFI_SUCCESS             The operation is completed.\r
+  @retval EFI_ALREADY_STARTED     Already in "connected" state, need to perform a disconnect\r
+                                  operation first.\r
+  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+  @retval Other Errors            Return errors when connecting network on low layer.\r
+\r
+**/\r
+EFI_STATUS\r
+WifiMgrConnectToNetwork (\r
+  IN    WIFI_MGR_DEVICE_DATA              *Nic,\r
+  IN    WIFI_MGR_NETWORK_PROFILE          *Profile\r
+  )\r
+{\r
+  EFI_STATUS                             Status;\r
+  EFI_TPL                                OldTpl;\r
+  EFI_ADAPTER_INFO_MEDIA_STATE           LinkState;\r
+  WIFI_MGR_MAC_CONFIG_TOKEN              *ConfigToken;\r
+  EFI_80211_CONNECT_NETWORK_TOKEN        *ConnectToken;\r
+\r
+  if (Nic == NULL || Nic->Wmp == NULL || Profile == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = WifiMgrGetLinkState (Nic, &LinkState);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  if (LinkState.MediaState == EFI_SUCCESS) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Status = WifiMgrPrepareConnection (Nic, Profile);\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Create a new connect token\r
+  //\r
+  ConfigToken = AllocateZeroPool (sizeof (WIFI_MGR_MAC_CONFIG_TOKEN));\r
+  if (ConfigToken == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Exit;\r
+  }\r
+\r
+  ConfigToken->Type      = TokenTypeConnectNetworkToken;\r
+  ConfigToken->Nic       = Nic;\r
+  ConfigToken->Token.ConnectNetworkToken  = AllocateZeroPool (sizeof (EFI_80211_CONNECT_NETWORK_TOKEN));\r
+  if (ConfigToken->Token.ConnectNetworkToken == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  ConnectToken           = ConfigToken->Token.ConnectNetworkToken;\r
+  ConnectToken->Data     = AllocateZeroPool (sizeof (EFI_80211_CONNECT_NETWORK_DATA));\r
+  if (ConnectToken->Data == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  ConnectToken->Data->Network = AllocateZeroPool (sizeof (EFI_80211_NETWORK));\r
+  if (ConnectToken->Data->Network == NULL) {\r
+    goto Exit;\r
+  }\r
+  CopyMem(ConnectToken->Data->Network, &Profile->Network, sizeof (EFI_80211_NETWORK));\r
+\r
+  //\r
+  // Add event handle and start to connect\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  WifiMgrOnConnectFinished,\r
+                  ConfigToken,\r
+                  &ConnectToken->Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Nic->ConnectState = WifiMgrConnectingToAp;\r
+  Nic->CurrentOperateNetwork = Profile;\r
+  WifiMgrUpdateConnectMessage (Nic, FALSE, NULL);\r
+\r
+  //\r
+  //Start Connecting ...\r
+  //\r
+  Status = Nic->Wmp->ConnectNetwork (Nic->Wmp, ConnectToken);\r
+\r
+  //\r
+  // Erase secrets after connection is triggered\r
+  //\r
+  WifiMgrCleanProfileSecrets (Profile);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_ALREADY_STARTED) {\r
+      Nic->ConnectState = WifiMgrConnectedToAp;\r
+      WifiMgrUpdateConnectMessage (Nic, TRUE, NULL);\r
+    } else {\r
+\r
+      Nic->ConnectState          = WifiMgrDisconnected;\r
+      Nic->CurrentOperateNetwork = NULL;\r
+\r
+      if (Nic->OneTimeConnectRequest) {\r
+        if (Status == EFI_NOT_FOUND) {\r
+          WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Not Available!");\r
+        } else {\r
+          WifiMgrUpdateConnectMessage (Nic, FALSE, L"Connect Failed: Unexpected Error!");\r
+        }\r
+      }\r
+    }\r
+    goto Exit;\r
+  }\r
+\r
+Exit:\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    WifiMgrFreeToken (ConfigToken);\r
+  }\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  DEBUG ((DEBUG_INFO, "[WiFi Connection Manager] WifiMgrConnectToNetwork: %r\n", Status));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The callback function for disconnect operation.\r
+\r
+  ASSERT when errors occur in config token.\r
+\r
+  @param[in]  Event                 The Disconnect token receive event.\r
+  @param[in]  Context               The context of the Disconnect token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+WifiMgrOnDisconnectFinished (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  WIFI_MGR_MAC_CONFIG_TOKEN         *ConfigToken;\r
+\r
+  ASSERT (Context != NULL);\r
+\r
+  ConfigToken = (WIFI_MGR_MAC_CONFIG_TOKEN*) Context;\r
+  ASSERT (ConfigToken->Nic != NULL);\r
+  ASSERT (ConfigToken->Type == TokenTypeDisconnectNetworkToken);\r
+\r
+  ASSERT (ConfigToken->Token.DisconnectNetworkToken != NULL);\r
+  if (ConfigToken->Token.DisconnectNetworkToken->Status != EFI_SUCCESS) {\r
+    ConfigToken->Nic->ConnectState          = WifiMgrConnectedToAp;\r
+    WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);\r
+    ConfigToken->Nic->OneTimeDisconnectRequest = FALSE;\r
+    goto Exit;\r
+  }\r
+\r
+  ConfigToken->Nic->ConnectState          = WifiMgrDisconnected;\r
+  ConfigToken->Nic->CurrentOperateNetwork = NULL;\r
+  WifiMgrUpdateConnectMessage (ConfigToken->Nic, TRUE, NULL);\r
+  ConfigToken->Nic->OneTimeDisconnectRequest = FALSE;\r
+\r
+  //\r
+  // Disconnected network may not be in network list now, trigger a scan again!\r
+  //\r
+  ConfigToken->Nic->OneTimeScanRequest       = TRUE;\r
+\r
+  Exit:\r
+    WifiMgrFreeToken(ConfigToken);\r
+    return;\r
+}\r
+\r
+/**\r
+  Start disconnect operation, and send out a token to disconnect from current connected\r
+  network.\r
+\r
+  @param[in]  Nic                 Pointer to the device data of the selected NIC.\r
+\r
+  @retval EFI_SUCCESS             The operation is completed.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.\r
+  @retval Other Errors            Return errors when disconnecting a network on low layer.\r
+\r
+**/\r
+EFI_STATUS\r
+WifiMgrDisconnectToNetwork (\r
+  IN    WIFI_MGR_DEVICE_DATA             *Nic\r
+  )\r
+{\r
+  EFI_STATUS                             Status;\r
+  EFI_TPL                                OldTpl;\r
+  WIFI_MGR_MAC_CONFIG_TOKEN              *ConfigToken;\r
+  EFI_80211_DISCONNECT_NETWORK_TOKEN     *DisconnectToken;\r
+\r
+  if (Nic == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl      = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Status      = EFI_SUCCESS;\r
+  ConfigToken = AllocateZeroPool (sizeof (WIFI_MGR_MAC_CONFIG_TOKEN));\r
+  if (ConfigToken == NULL) {\r
+    gBS->RestoreTPL (OldTpl);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ConfigToken->Type      = TokenTypeDisconnectNetworkToken;\r
+  ConfigToken->Nic       = Nic;\r
+  ConfigToken->Token.DisconnectNetworkToken = AllocateZeroPool (sizeof (EFI_80211_DISCONNECT_NETWORK_TOKEN));\r
+  if (ConfigToken->Token.DisconnectNetworkToken == NULL) {\r
+    WifiMgrFreeToken(ConfigToken);\r
+    gBS->RestoreTPL (OldTpl);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  DisconnectToken = ConfigToken->Token.DisconnectNetworkToken;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  WifiMgrOnDisconnectFinished,\r
+                  ConfigToken,\r
+                  &DisconnectToken->Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    WifiMgrFreeToken(ConfigToken);\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  Nic->ConnectState = WifiMgrDisconnectingToAp;\r
+  WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);\r
+\r
+  Status = Nic->Wmp->DisconnectNetwork (Nic->Wmp, DisconnectToken);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_NOT_FOUND) {\r
+\r
+      Nic->ConnectState          = WifiMgrDisconnected;\r
+      Nic->CurrentOperateNetwork = NULL;\r
+\r
+      //\r
+      // This network is not in network list now, trigger a scan again!\r
+      //\r
+      Nic->OneTimeScanRequest    = TRUE;\r
+\r
+      //\r
+      // State has been changed from Connected to Disconnected\r
+      //\r
+      WifiMgrUpdateConnectMessage (ConfigToken->Nic, TRUE, NULL);\r
+      Status                     = EFI_SUCCESS;\r
+    } else {\r
+      if (Nic->OneTimeDisconnectRequest) {\r
+\r
+        WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, L"Disconnect Failed: Unexpected Error!");\r
+      }\r
+\r
+      Nic->ConnectState     = WifiMgrConnectedToAp;\r
+      WifiMgrUpdateConnectMessage (ConfigToken->Nic, FALSE, NULL);\r
+    }\r
+    WifiMgrFreeToken(ConfigToken);\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The state machine of the connection manager, periodically check the state and\r
+  perform a corresponding operation.\r
+\r
+  @param[in]  Event                   The timer event to be triggered.\r
+  @param[in]  Context                 The context of the Nic device data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+WifiMgrOnTimerTick (\r
+  IN EFI_EVENT                        Event,\r
+  IN VOID                             *Context\r
+  )\r
+{\r
+  WIFI_MGR_DEVICE_DATA                *Nic;\r
+  EFI_STATUS                          Status;\r
+  EFI_ADAPTER_INFO_MEDIA_STATE        LinkState;\r
+  WIFI_MGR_NETWORK_PROFILE            *Profile;\r
+\r
+  if (Context == NULL) {\r
+    return;\r
+  }\r
+\r
+  Nic = (WIFI_MGR_DEVICE_DATA*) Context;\r
+  NET_CHECK_SIGNATURE (Nic, WIFI_MGR_DEVICE_DATA_SIGNATURE);\r
+\r
+  Status = WifiMgrGetLinkState (Nic, &LinkState);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_INFO, "[WiFi Connection Manager] Error: Failed to get link state!\n"));\r
+    return;\r
+  }\r
+\r
+  if (Nic->LastLinkState.MediaState != LinkState.MediaState) {\r
+    if (Nic->LastLinkState.MediaState == EFI_SUCCESS && LinkState.MediaState == EFI_NO_MEDIA) {\r
+      Nic->HasDisconnectPendingNetwork = TRUE;\r
+    }\r
+    Nic->LastLinkState.MediaState = LinkState.MediaState;\r
+  }\r
+\r
+  Nic->ScanTickTime ++;\r
+  if ((Nic->ScanTickTime > WIFI_SCAN_FREQUENCY || Nic->OneTimeScanRequest) &&\r
+    Nic->ScanState == WifiMgrScanFinished) {\r
+\r
+    Nic->OneTimeScanRequest = FALSE;\r
+    Nic->ScanTickTime = 0;\r
+\r
+    DEBUG ((DEBUG_INFO, "[WiFi Connection Manager] Scan is triggered.\n"));\r
+    WifiMgrStartScan (Nic);\r
+  }\r
+\r
+  if (Nic->AvailableCount > 0 && Nic->ScanState == WifiMgrScanFinished) {\r
+\r
+    switch (Nic->ConnectState) {\r
+    case WifiMgrDisconnected:\r
+\r
+      if (Nic->HasDisconnectPendingNetwork) {\r
+        Nic->HasDisconnectPendingNetwork = FALSE;\r
+      }\r
+\r
+      if (Nic->ConnectPendingNetwork != NULL) {\r
+\r
+        Profile   = Nic->ConnectPendingNetwork;\r
+        Status    = WifiMgrConnectToNetwork(Nic, Profile);\r
+        Nic->ConnectPendingNetwork = NULL;\r
+        if (EFI_ERROR (Status)) {\r
+          //\r
+          // Some error happened, don't wait for a return connect token!\r
+          //\r
+          Nic->OneTimeConnectRequest = FALSE;\r
+        }\r
+      }\r
+      break;\r
+\r
+    case WifiMgrConnectingToAp:\r
+      break;\r
+\r
+    case WifiMgrDisconnectingToAp:\r
+      break;\r
+\r
+    case WifiMgrConnectedToAp:\r
+\r
+      if (Nic->ConnectPendingNetwork != NULL || Nic->HasDisconnectPendingNetwork) {\r
+\r
+        Status    = WifiMgrDisconnectToNetwork(Nic);\r
+        if (EFI_ERROR (Status)) {\r
+          //\r
+          // Some error happened, don't wait for a return disconnect token!\r
+          //\r
+          Nic->OneTimeDisconnectRequest = FALSE;\r
+        }\r
+      }\r
+      break;\r
+\r
+    default:\r
+      break;\r
+    }\r
+  }\r
+}\r