wlan: Fix ULA Logic to remove EAPOL 4/4 race-condition.

Call ULA done callback from TL when it receives
EAPOL 4/4 packet. ULA done callback unblocks
set key to go through. Remove ULA logic from
SAP mode as it doesn't have race-condition.

Change-Id: Id5e08378019bb31f1829b257d32855a051ac4dcf
CRs-Fixed: 784553
diff --git a/CORE/HDD/inc/wlan_hdd_tx_rx.h b/CORE/HDD/inc/wlan_hdd_tx_rx.h
index a94dacf..acad7ab 100644
--- a/CORE/HDD/inc/wlan_hdd_tx_rx.h
+++ b/CORE/HDD/inc/wlan_hdd_tx_rx.h
@@ -49,6 +49,14 @@
 #define HDD_ETHERTYPE_802_1_X              ( 0x888E )
 #define HDD_ETHERTYPE_802_1_X_FRAME_OFFSET ( 12 )
 #define HDD_ETHERTYPE_802_1_X_SIZE         ( 2 )
+
+#define HDD_ETHERTYPE_802_1_X_FRAME_SUB_TYPE_OFFSET ( 19 )
+#define HDD_ETHERTYPE_802_1_X_SUB_TYPE_MASK ( 0x8003 )
+#define HDD_ETHERTYPE_802_1_X_M1_VALUE      ( 0x8000 )
+#define HDD_ETHERTYPE_802_1_X_M2_VALUE      ( 0x0001 )
+#define HDD_ETHERTYPE_802_1_X_M3_VALUE      ( 0x8003 )
+#define HDD_ETHERTYPE_802_1_X_M4_VALUE      ( 0x0003 )
+
 #ifdef FEATURE_WLAN_WAPI
 #define HDD_ETHERTYPE_WAI                  ( 0x88b4 )
 #endif
@@ -266,6 +274,14 @@
 extern v_BOOL_t hdd_IsEAPOLPacket( vos_pkt_t *pVosPacket );
 
 /**============================================================================
+  @brief hdd_FindEapolSubType() - Find EAPOL SubType.
+
+  @param pVosPacket : [in] pointer to vos packet
+  @return         : EAPOL_SubType value
+  ===========================================================================*/
+extern EAPOL_SubType hdd_FindEapolSubType( vos_pkt_t *pVosPacket );
+
+/**============================================================================
   @brief hdd_mon_tx_mgmt_pkt() - Transmit MGMT packet received on monitor 
                                  interface.
 
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index b022c69..ac4083d 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -8356,21 +8356,6 @@
         pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
         if( pHostapdState->bssState == BSS_START )
         {
-            hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
-            vos_status = wlan_hdd_check_ula_done(pAdapter);
-
-            if ( vos_status != VOS_STATUS_SUCCESS )
-            {
-                VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
-                        "[%4d] wlan_hdd_check_ula_done returned ERROR status= %d",
-                        __LINE__, vos_status );
-
-                pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
-
-                status = -EINVAL;
-                goto end;
-            }
-
             status = WLANSAP_SetKeySta( pVosContext, &setKey);
 
             if ( status != eHAL_STATUS_SUCCESS )
@@ -8438,19 +8423,36 @@
                 setKey.peerMac[4], setKey.peerMac[5],
                 setKey.keyDirection);
 
-        vos_status = wlan_hdd_check_ula_done(pAdapter);
-
-        if ( vos_status != VOS_STATUS_SUCCESS )
+        /* Wait for EAPOL M4 before setting key.
+         * No need to consider Dynamic WEP as we will receive M8.
+         */
+        if ( (setKey.encType == eCSR_ENCRYPT_TYPE_AES ||
+             setKey.encType == eCSR_ENCRYPT_TYPE_TKIP) &&
+             ( 1
+#if defined WLAN_FEATURE_VOWIFI_11R
+               && pHddStaCtx->conn_info.authType != eCSR_AUTH_TYPE_FT_RSN
+               && pHddStaCtx->conn_info.authType != eCSR_AUTH_TYPE_FT_RSN_PSK
+#endif
+#ifdef FEATURE_WLAN_ESE
+               && pHddStaCtx->conn_info.authType != eCSR_AUTH_TYPE_CCKM_WPA
+               && pHddStaCtx->conn_info.authType != eCSR_AUTH_TYPE_CCKM_RSN
+#endif
+               ))
         {
-            VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            vos_status = wlan_hdd_check_ula_done(pAdapter);
+
+            if ( vos_status != VOS_STATUS_SUCCESS )
+            {
+               VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                     "[%4d] wlan_hdd_check_ula_done returned ERROR status= %d",
                     __LINE__, vos_status );
 
-            pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
+               pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
 
-            status = -EINVAL;
-            goto end;
+               status = -EINVAL;
+               goto end;
 
+            }
         }
 
 #ifdef WLAN_FEATURE_VOWIFI_11R
diff --git a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
index 692e256..2db04f4 100644
--- a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
@@ -1360,6 +1360,7 @@
          VOS_TRACE( VOS_MODULE_ID_HDD_SAP_DATA, VOS_TRACE_LEVEL_INFO_HIGH,
                     "%s: VOS packet is EAPOL packet", __func__);
          pPktMetaInfo->ucIsEapol = 1;
+         pPktMetaInfo->ucEapolSubType = hdd_FindEapolSubType(pVosPacket);
       }
    }
  
@@ -1371,7 +1372,7 @@
       if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
       {
          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                   "SAP TX EAPOL");
+                   "SAP TX EAPOL SubType %d",pPktMetaInfo->ucEapolSubType);
       }
       else if (VOS_PKT_PROTO_TYPE_DHCP & proto_type)
       {
@@ -1526,6 +1527,7 @@
    vos_pkt_t* pNextVosPacket;   
    hdd_context_t *pHddCtx = NULL;   
    v_U8_t proto_type;
+   EAPOL_SubType eapolSubType;
 
    //Sanity check on inputs
    if ( ( NULL == vosContext ) || 
@@ -1593,6 +1595,12 @@
          return VOS_STATUS_E_FAILURE;
       }
 
+      if (pHddCtx->cfg_ini->gEnableDebugLog)
+      {
+         if(hdd_IsEAPOLPacket(pVosPacket))
+            eapolSubType = hdd_FindEapolSubType(pVosPacket);
+      }
+
       // Extract the OS packet (skb).
       // Tell VOS to detach the OS packet from the VOS packet
       status = vos_pkt_get_os_packet( pVosPacket, (v_VOID_t **)&skb, VOS_TRUE );
@@ -1625,7 +1633,7 @@
          if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
          {
             VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                      "SAP RX EAPOL");
+                      "SAP RX EAPOL SubType %d",eapolSubType);
          }
          else if (VOS_PKT_PROTO_TYPE_DHCP & proto_type)
          {
diff --git a/CORE/HDD/src/wlan_hdd_tx_rx.c b/CORE/HDD/src/wlan_hdd_tx_rx.c
index 370fd71..eab8bf8 100644
--- a/CORE/HDD/src/wlan_hdd_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_tx_rx.c
@@ -967,7 +967,9 @@
       status = hdd_wmm_acquire_access( pAdapter, ac, &granted );
       pAdapter->psbChanged |= (1 << ac);
    }
-   if ( granted && ( pktListSize == 1 ))
+
+   if ( (granted && ( pktListSize == 1 )) ||
+        (pHddStaCtx->conn_info.uIsAuthenticated == VOS_FALSE))
    {
       //Let TL know we have a packet to send for this AC
       //VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s:Indicating Packet to TL", __func__);
@@ -1316,6 +1318,50 @@
 }
 
 /**============================================================================
+  @brief hdd_FindEapolSubType() - Find EAPOL SubType.
+
+  @param pVosPacket : [in] pointer to vos packet
+  @return         : EAPOL_SubType value
+  ===========================================================================*/
+EAPOL_SubType hdd_FindEapolSubType( vos_pkt_t *pVosPacket )
+{
+    VOS_STATUS vosStatus  = VOS_STATUS_SUCCESS;
+    void       *pBuffer   = NULL;
+    EAPOL_SubType   subType = EAPOL_UNKNOWN;
+    v_U16_t   keyInfo;
+    vosStatus = vos_pkt_peek_data( pVosPacket,
+                         (v_SIZE_t)HDD_ETHERTYPE_802_1_X_FRAME_SUB_TYPE_OFFSET,
+                         &pBuffer, HDD_ETHERTYPE_802_1_X_SIZE );
+    if ( VOS_IS_STATUS_SUCCESS( vosStatus ) )
+    {
+       if ( pBuffer )
+       {
+          keyInfo = (*(unsigned short*)pBuffer &
+                         HDD_ETHERTYPE_802_1_X_SUB_TYPE_MASK);
+
+          switch (keyInfo) {
+            case HDD_ETHERTYPE_802_1_X_M1_VALUE:
+                 subType = EAPOL_M1;
+                 break;
+            case HDD_ETHERTYPE_802_1_X_M2_VALUE:
+                 subType = EAPOL_M2;
+                 break;
+            case HDD_ETHERTYPE_802_1_X_M3_VALUE:
+                 subType = EAPOL_M3;
+                 break;
+            case HDD_ETHERTYPE_802_1_X_M4_VALUE:
+                 subType = EAPOL_M4;
+                 break;
+            default:
+                 break;
+         }
+       }
+    }
+
+   return subType;
+}
+
+/**============================================================================
   @brief hdd_IsARP() - Checks the packet is ARP or not.
 
   @param pVosPacket : [in] pointer to vos packet
@@ -1671,8 +1717,12 @@
    
    if(pAdapter->sessionCtx.station.conn_info.uIsAuthenticated == VOS_TRUE)
       pPktMetaInfo->ucIsEapol = 0;       
-   else 
+   else
+   {
       pPktMetaInfo->ucIsEapol = hdd_IsEAPOLPacket( pVosPacket ) ? 1 : 0;
+      if(pPktMetaInfo->ucIsEapol)
+         pPktMetaInfo->ucEapolSubType = hdd_FindEapolSubType( pVosPacket );
+   }
 
    if ((NULL != pHddCtx) &&
        (pHddCtx->cfg_ini->gEnableDebugLog))
@@ -1682,7 +1732,7 @@
       if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
       {
          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                   "STA TX EAPOL");
+                   "STA TX EAPOL SubType %d",pPktMetaInfo->ucEapolSubType);
       }
       else if (VOS_PKT_PROTO_TYPE_DHCP & proto_type)
       {
@@ -1933,6 +1983,7 @@
    vos_pkt_t* pVosPacket;
    vos_pkt_t* pNextVosPacket;
    v_U8_t proto_type;
+   EAPOL_SubType eapolSubType;
 
    //Sanity check on inputs
    if ( ( NULL == vosContext ) || 
@@ -1980,6 +2031,12 @@
          return VOS_STATUS_E_FAILURE;
       }
 
+      if (pHddCtx->cfg_ini->gEnableDebugLog)
+      {
+         if (hdd_IsEAPOLPacket(pVosPacket))
+             eapolSubType = hdd_FindEapolSubType(pVosPacket);
+      }
+
       // Extract the OS packet (skb).
       // Tell VOS to detach the OS packet from the VOS packet
       status = vos_pkt_get_os_packet( pVosPacket, (v_VOID_t **)&skb, VOS_TRUE );
@@ -2038,7 +2095,7 @@
          if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
          {
             VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                      "STA RX EAPOL");
+                      "STA RX EAPOL SubType %d",eapolSubType);
          }
          else if (VOS_PKT_PROTO_TYPE_DHCP & proto_type)
          {
diff --git a/CORE/HDD/src/wlan_hdd_wext.c b/CORE/HDD/src/wlan_hdd_wext.c
index e751579..9e208d1 100644
--- a/CORE/HDD/src/wlan_hdd_wext.c
+++ b/CORE/HDD/src/wlan_hdd_wext.c
@@ -1279,9 +1279,15 @@
         /*To avoid race condition between the set key and the last EAPOL
           packet, notify TL to finish upper layer authentication incase if the
           last EAPOL packet pending in the TL queue.*/
-        vos_status = WLANTL_Finish_ULA(wlan_hdd_ula_done_cb, pAdapter);
+        vos_status = WLANTL_Finish_ULA(wlan_hdd_ula_done_cb, pAdapter,
+                        (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
+                        pHddStaCtx->conn_info.staId[0]);
 
-        if ( vos_status != VOS_STATUS_SUCCESS )
+        if ( vos_status == VOS_STATUS_E_ALREADY )
+        {
+            return VOS_STATUS_SUCCESS;
+        }
+        else if ( vos_status != VOS_STATUS_SUCCESS )
         {
             VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                    "[%4d] WLANTL_Finish_ULA returned ERROR status= %d",
diff --git a/CORE/TL/inc/wlan_qct_tl.h b/CORE/TL/inc/wlan_qct_tl.h
index 7070ed2..fec61af 100644
--- a/CORE/TL/inc/wlan_qct_tl.h
+++ b/CORE/TL/inc/wlan_qct_tl.h
@@ -391,6 +391,18 @@
 }WLANTL_STAPriorityType;
 
 /*---------------------------------------------------------------------------
+  EAPOL SUB TYPE
+---------------------------------------------------------------------------*/
+typedef enum
+{
+  EAPOL_UNKNOWN  = 0,
+  EAPOL_M1,
+  EAPOL_M2,
+  EAPOL_M3,
+  EAPOL_M4
+}EAPOL_SubType;
+
+/*---------------------------------------------------------------------------
   Meta information requested from HDD by TL 
 ---------------------------------------------------------------------------*/      
 typedef struct
@@ -403,6 +415,10 @@
 
   /* notifying TL if this is an EAPOL frame or not */
   v_U8_t    ucIsEapol;
+
+  /* Store Eapol Subtype */
+  EAPOL_SubType    ucEapolSubType;
+
 #ifdef FEATURE_WLAN_WAPI
   /* notifying TL if this is a WAI frame or not */
   v_U8_t    ucIsWai;
@@ -678,6 +694,22 @@
 
 /*----------------------------------------------------------------------------
 
+  DESCRIPTION
+    ULA callback registered with TL.
+    It is called by the TL when it receives EAPOL 4/4.
+
+
+  PARAMETERS
+
+    IN
+    callbackCtx:    padapter context used in callback.
+
+----------------------------------------------------------------------------*/
+
+typedef VOS_STATUS (*WLANTL_STAUlaCompleteCBType)( v_PVOID_t callbackCtx );
+
+/*----------------------------------------------------------------------------
+
   DESCRIPTION   
     Type of the receive callback registered with TL. 
     
@@ -2888,16 +2920,16 @@
   FUNCTION    WLANTL_Finish_ULA
 
   DESCRIPTION
-     This function is used by HDD to notify TL to finish Upper layer authentication
-     incase the last EAPOL packet is pending in the TL queue. 
-     To avoid the race condition between sme set key and the last EAPOL packet 
+     This function is used by HDD to notify TL to finish Upper layer
+     authentication incase the last EAPOL packet is pending in the TL queue.
+     To avoid the race condition between sme set key and the last EAPOL packet
      the HDD module calls this function just before calling the sme_RoamSetKey.
-   
+
   DEPENDENCIES
 
     TL must have been initialized before this gets called.
 
-   
+
   PARAMETERS
 
    callbackRoutine:   HDD Callback function.
@@ -2906,13 +2938,15 @@
   RETURN VALUE
 
    VOS_STATUS_SUCCESS/VOS_STATUS_FAILURE
-   
+
   SIDE EFFECTS
-   
+
 ============================================================================*/
 
 VOS_STATUS WLANTL_Finish_ULA( void (*callbackRoutine) (void *callbackContext),
-                              void *callbackContext);
+                              void *callbackContext,
+                              v_PVOID_t pvosGCtx,
+                              v_U8_t ucSTAId);
 
 /*===============================================================================
   FUNCTION       WLANTL_UpdateRssiBmps
diff --git a/CORE/TL/src/wlan_qct_tl.c b/CORE/TL/src/wlan_qct_tl.c
index 2a26ce3..1d4a815 100644
--- a/CORE/TL/src/wlan_qct_tl.c
+++ b/CORE/TL/src/wlan_qct_tl.c
@@ -1083,8 +1083,8 @@
        " %s fails to start forwarding (staId %d)", __func__, staId);
   }
 }
-  
-  /*===========================================================================
+
+/*===========================================================================
 
   FUNCTION  WLANTL_Finish_ULA
 
@@ -1097,26 +1097,72 @@
   DEPENDENCIES
 
      TL must have been initialized before this gets called.
-  
+
   PARAMETERS
 
-   callbackRoutine:   HDD Callback function.
-   callbackContext : HDD userdata context.
-  
+   callbackRoutine: HDD Callback function.
+   callbackContext: HDD userdata context.
+   pvosGCtx       : VOS Context
+   ucSTAId        : STA ID
+
    RETURN VALUE
 
    VOS_STATUS_SUCCESS/VOS_STATUS_FAILURE
-   
+
   SIDE EFFECTS
-   
+
 ============================================================================*/
 
 VOS_STATUS WLANTL_Finish_ULA( void (*callbackRoutine) (void *callbackContext),
-                              void *callbackContext)
+                                      void *callbackContext,
+                                      v_PVOID_t pvosGCtx,
+                                      v_U8_t ucSTAId)
 {
-   return WDA_DS_FinishULA( callbackRoutine, callbackContext);
-}
+  WLANTL_CbType* pTLCb = NULL;
+  WLANTL_STAClientType* pClientSTA = NULL;
 
+  /*------------------------------------------------------------------------
+      Sanity check
+   ------------------------------------------------------------------------*/
+  if ( WLANTL_STA_ID_INVALID( ucSTAId ) )
+  {
+     TLLOGE(VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_ERROR,
+            "WLAN TL:Invalid station id requested on WLANTL_Finish_ULA"));
+     return VOS_STATUS_E_FAULT;
+  }
+
+  /*------------------------------------------------------------------------
+     Extract TL control block and check existance
+    ------------------------------------------------------------------------*/
+  pTLCb = VOS_GET_TL_CB(pvosGCtx);
+  if ( NULL == pTLCb )
+  {
+    TLLOGE(VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_ERROR,
+           "WLAN TL:Invalid TL pointer from pvosGCtx on WLANTL_Finish_ULA"));
+    return VOS_STATUS_E_FAULT;
+  }
+
+  pClientSTA = pTLCb->atlSTAClients[ucSTAId];
+  if ( NULL == pClientSTA )
+  {
+    TLLOGE(VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_ERROR,
+           "WLAN TL:Client Memory was not allocated on %s", __func__));
+    return VOS_STATUS_E_FAILURE;
+  }
+
+  if (pClientSTA->isEapolM4Transmitted ||
+      pClientSTA->tlState == WLANTL_STA_AUTHENTICATED)
+  {
+    TLLOGE(VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_INFO,
+           "WLAN TL: M4 is already received on %s", __func__));
+    return VOS_STATUS_E_ALREADY;
+  }
+
+  pClientSTA->pfnSTAUlaComplete = (WLANTL_STAUlaCompleteCBType) callbackRoutine;
+  pClientSTA->pUlaCBCtx = callbackContext;
+
+  return VOS_STATUS_SUCCESS;
+}
 
 /*===========================================================================
 
@@ -1247,6 +1293,9 @@
   pClientSTA->pfnSTARx       = pfnSTARx;
   pClientSTA->pfnSTAFetchPkt = pfnSTAFetchPkt;
 
+  pClientSTA->pfnSTAUlaComplete = NULL;
+  pClientSTA->pUlaCBCtx         = NULL;
+
   /* Only register if different from NULL - TL default Tx Comp Cb will
     release the vos packet */
   if ( NULL != pfnSTATxComp )
@@ -1258,6 +1307,7 @@
   pClientSTA->tlPri    = WLANTL_STA_PRI_NORMAL;
   pClientSTA->wSTADesc.ucSTAId  = pwSTADescType->ucSTAId;
   pClientSTA->ptkInstalled = 0;
+  pClientSTA->isEapolM4Transmitted = 0;
 
   TLLOG2(VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_INFO_HIGH,
              "WLAN TL:Registering STA Client ID: %d with UC %d and BC %d", 
@@ -2072,6 +2122,7 @@
 {
   WLANTL_CbType*  pTLCb = NULL;
   WLANTL_STAClientType* pClientSTA = NULL;
+  v_U32_t     uMgmtAvailRes = 0;
   /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
 
   VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_INFO,
@@ -2135,6 +2186,9 @@
     VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_INFO_HIGH,
       "WLAN TL:Packet pending indication for STA: %d AC: %d State: %d", 
                ucSTAId, ucAc, pClientSTA->tlState);
+
+    uMgmtAvailRes = WDA_DS_GetAvailableResCount(pvosGCtx,
+                                              WDI_MGMT_POOL_ID);
   }
 
   /*-----------------------------------------------------------------------
@@ -2157,8 +2211,10 @@
         Check if there are enough resources for transmission and tx is not
         suspended.
         ------------------------------------------------------------------------*/
-       if (( pTLCb->uResCount >=  WDA_TLI_MIN_RES_DATA ) &&
-          ( 0 == pTLCb->ucTxSuspended ))
+       if ((( pTLCb->uResCount >=  WDA_TLI_MIN_RES_DATA ) &&
+           ( 0 == pTLCb->ucTxSuspended )) ||
+           (pClientSTA->ucEapolPktPending &&
+            uMgmtAvailRes >= WDA_TLI_MIN_RES_MF))
       {
 
         TLLOG2(VOS_TRACE( VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_INFO_HIGH,
@@ -4410,6 +4466,7 @@
           {
             pTLCb->atlSTAClients[ucSTAId]->ucCurrentAC = j-1;
             pTLCb->uCurServedAC = j-1;
+            break;
           }
         }
 
@@ -7586,6 +7643,18 @@
     return vosStatus;
   }
 
+
+
+  // call ULA complete once M4 BD is filled.
+  if (tlMetaInfo.ucEapolSubType == EAPOL_M4)
+  {
+     pClientSTA->isEapolM4Transmitted = 1;
+     if (pClientSTA->pfnSTAUlaComplete)
+         pClientSTA->pfnSTAUlaComplete(pClientSTA->pUlaCBCtx);
+     pClientSTA->pfnSTAUlaComplete = NULL;
+     pClientSTA->pUlaCBCtx = NULL;
+  }
+
   /*-----------------------------------------------------------------------
     Update tx counter for BA session query for tx side
     !1 - should this be done for EAPOL frames?
@@ -11437,6 +11506,10 @@
   ptlSTAClient->wSTADesc.ucSwFrameRXXlation = 0;
   ptlSTAClient->wSTADesc.ucProtectedFrame = 0;
 
+  ptlSTAClient->pfnSTAUlaComplete = NULL;
+  ptlSTAClient->pUlaCBCtx = NULL;
+  ptlSTAClient->isEapolM4Transmitted = 0;
+
   /*-------------------------------------------------------------------------
     AMSDU information for the STA
    -------------------------------------------------------------------------*/
diff --git a/CORE/TL/src/wlan_qct_tli.h b/CORE/TL/src/wlan_qct_tli.h
index da5f3e4..704a51a 100644
--- a/CORE/TL/src/wlan_qct_tli.h
+++ b/CORE/TL/src/wlan_qct_tli.h
@@ -519,6 +519,12 @@
   /* Function pointer to the packet retrieval routine in HDD */
   WLANTL_STAFetchPktCBType      pfnSTAFetchPkt;
 
+  /* Function pointer holding ULA complete CB routine registered by HDD */
+  WLANTL_STAUlaCompleteCBType   pfnSTAUlaComplete;
+
+  /* HDD Context used in ULA complete CB routine  */
+  v_PVOID_t                     pUlaCBCtx;
+
   /* Reordering information for the STA */
   WLANTL_BAReorderType          atlBAReorderInfo[WLAN_MAX_TID];
 
@@ -680,6 +686,9 @@
     state (CONNECTED/AUTHENTICATED) */
   v_U8_t ptkInstalled;
 
+  /* Flag to check EAPOL 4/4 recevied by TL*/
+  v_U8_t isEapolM4Transmitted;
+
   v_U32_t       linkCapacity;
 
 #ifdef WLAN_FEATURE_LINK_LAYER_STATS
diff --git a/CORE/WDA/inc/wlan_qct_wda.h b/CORE/WDA/inc/wlan_qct_wda.h
index c1d3955..532de67 100644
--- a/CORE/WDA/inc/wlan_qct_wda.h
+++ b/CORE/WDA/inc/wlan_qct_wda.h
@@ -1906,6 +1906,32 @@
 );
 
 /*==========================================================================
+   FUNCTION    WDA_DS_GetAvailableResCount
+
+  DESCRIPTION
+  It returns Available resource count for appropriate Pool Type
+
+  DEPENDENCIES
+
+  PARAMETERS
+
+   IN
+    pvosGCtx          vos context
+    wdiResPool       Pool Type
+
+  RETURN VALUE
+    Available resource count
+
+============================================================================*/
+uint32
+WDA_DS_GetAvailableResCount
+(
+  v_PVOID_t pvosGCtx,
+  WDI_ResPoolType wdiResPool
+);
+
+
+/*==========================================================================
    FUNCTION    WDA_HALDumpCmdReq
 
   DESCRIPTION
diff --git a/CORE/WDA/src/wlan_qct_wda_ds.c b/CORE/WDA/src/wlan_qct_wda_ds.c
index 573dae7..603675e 100644
--- a/CORE/WDA/src/wlan_qct_wda_ds.c
+++ b/CORE/WDA/src/wlan_qct_wda_ds.c
@@ -1208,3 +1208,40 @@
 
   wdaContext->pfnTxCompleteCallback( pvosGCtx, pFrameDataBuff, vosStatus );
 }
+
+/*==========================================================================
+   FUNCTION    WDA_DS_GetAvailableResCount
+
+  DESCRIPTION
+  It returns Available resource count for appropriate Pool Type
+
+  DEPENDENCIES
+
+  PARAMETERS
+
+   IN
+    pvosGCtx          vos context
+    wdiResPool       Pool Type
+
+  RETURN VALUE
+    Available resource count
+
+============================================================================*/
+uint32
+WDA_DS_GetAvailableResCount
+(
+  v_PVOID_t pvosGCtx,
+  WDI_ResPoolType wdiResPool
+)
+{
+  tWDA_CbContext *wdaContext = NULL;
+  wdaContext = (tWDA_CbContext *)vos_get_context(VOS_MODULE_ID_WDA, pvosGCtx);
+  if ( NULL == wdaContext )
+  {
+       VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+           "WDA:Invalid wda context pointer from pvosGCtx on WDA_DS_GetAvailableResCount" );
+       return 0;
+  }
+  return WDI_GetAvailableResCount(wdaContext->pWdiContext,wdiResPool);
+}
+