wlan: Check SAP_OFFLOADS capability before SAP open

If the ini gEnableSAPAuthOffload is set and firmware doesn't support
SAP_OFFLOADS then effectively driver should not support sap auth
offloads feature.

But from hdd_wlan_startup(), softap is created with sap auth offloads
feature using gEnableSAPAuthOffload only. This leads to out of sync
between firmware and driver thereby causing kernel panic with assert
when driver is expecting auth complete but firmware is giving auth
request notifcation.

To address this, validate firmware SAP_OFFLOADS capability before
softap create.

Change-Id: Ie078a26660ed3823cb26eae8c010511bb5f96410
CRs-Fixed: 2403630
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 3e473cf..e8ab954 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -134,6 +134,8 @@
 #define FW_STATE_WAIT_TIME 500
 #define FW_STATE_RSP_LEN 100
 
+#define WLAN_WAIT_TIME_FEATURE_CAPS 300
+
 /* Amount of time to wait for sme close session callback.
    This value should be larger than the timeout used by WDI to wait for
    a response from WCNSS */
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index e3686e8..bb3bcd8 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -12649,6 +12649,80 @@
 
 }
 
+/**
+ * hdd_get_feature_caps_cb() - Callback invoked from WDA
+ * @cookie: to identify HDD request to firmware
+ *
+ * This function is invoked from WDA when feature capabilities response
+ * is received from firmware.
+ *
+ * Return: None
+ */
+static void hdd_get_feature_caps_cb(void *cookie)
+{
+	struct hdd_request *request;
+
+	request = hdd_request_get(cookie);
+	if (!request) {
+		hddLog(VOS_TRACE_LEVEL_ERROR, FL("Obsolete request"));
+		return;
+	}
+
+	pr_info("%s: Firmware feature capabilities received\n", __func__);
+
+	hdd_request_complete(request);
+	hdd_request_put(request);
+}
+
+/**
+ * hdd_get_feature_caps() - Get features supported by firmware
+ * @hdd_ctx: Pointer to HDD context
+ *
+ * This function uses request manager framework to get the feature
+ * capabilities from firmware.
+ *
+ * Return: None
+ */
+static void hdd_get_feature_caps(hdd_context_t *hdd_ctx)
+{
+	VOS_STATUS status;
+	void *cookie;
+	int ret;
+	struct hdd_request *request;
+	static const struct hdd_request_params params = {
+		.priv_size = 0,
+		.timeout_ms = WLAN_WAIT_TIME_FEATURE_CAPS,
+	};
+	struct sir_feature_caps_params caps_params = {0};
+
+	request = hdd_request_alloc(&params);
+	if (!request) {
+		pr_err("%s: Request allocation failure\n", __func__);
+		return;
+	}
+
+	cookie = hdd_request_cookie(request);
+	caps_params.user_data = cookie;
+	caps_params.feature_caps_cb = hdd_get_feature_caps_cb;
+
+	status = sme_featureCapsExchange(&caps_params);
+	if (status != VOS_STATUS_SUCCESS) {
+		pr_err("%s: Unable to get feature caps\n", __func__);
+		goto end;
+	}
+
+	/* request was sent -- wait for the response */
+	ret = hdd_request_wait_for_response(request);
+	if (ret) {
+		pr_err("%s: SME timeout while retrieving feature caps\n",
+			__func__);
+		goto end;
+	}
+
+end:
+	hdd_request_put(request);
+}
+
 /**---------------------------------------------------------------------------
 
   \brief hdd_exchange_version_and_caps() - HDD function to exchange version and capability
@@ -12783,12 +12857,13 @@
             sme_disableFeatureCapablity(IBSS_HEARTBEAT_OFFLOAD);
          }
 
-         sme_featureCapsExchange(pHddCtx->hHal);
+         hdd_get_feature_caps(pHddCtx);
       }
 
    } while (0);
 
 }
+
 void wlan_hdd_send_svc_nlink_msg(int type, void *data, int len)
 {
        struct sk_buff *skb;
@@ -13055,7 +13130,7 @@
    }
 
    WLANTL_SetMonRxCbk( pVosContext, hdd_rx_packet_monitor_cbk );
-   WDA_featureCapsExchange(pVosContext);
+   sme_featureCapsExchange(NULL);
    wcnss_wlan_set_drvdata(pHddCtx->parent_dev, pHddCtx);
 
    pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_MONITOR, "wlan%d",
@@ -13897,6 +13972,14 @@
 
    wcnss_wlan_set_drvdata(pHddCtx->parent_dev, pHddCtx);
 
+#ifdef SAP_AUTH_OFFLOAD
+   if (!sme_IsFeatureSupportedByFW(SAP_OFFLOADS))
+   {
+       hddLog(VOS_TRACE_LEVEL_INFO, FL(" SAP AUTH OFFLOAD not supp by FW"));
+       pHddCtx->cfg_ini->enable_sap_auth_offload = 0;
+   }
+#endif
+
    if (VOS_STA_SAP_MODE == hdd_get_conparam())
    {
      pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_SOFTAP, "softap.%d", 
@@ -14179,14 +14262,6 @@
 
 #endif
 
-#ifdef SAP_AUTH_OFFLOAD
-   if (!sme_IsFeatureSupportedByFW(SAP_OFFLOADS))
-   {
-       hddLog(VOS_TRACE_LEVEL_INFO, FL(" SAP AUTH OFFLOAD not supp by FW"));
-       pHddCtx->cfg_ini->enable_sap_auth_offload = 0;
-   }
-#endif
-
    if (vos_is_multicast_logging())
        wlan_logging_set_log_level();
 
diff --git a/CORE/MAC/inc/sirApi.h b/CORE/MAC/inc/sirApi.h
index f665446..4f7e7db 100644
--- a/CORE/MAC/inc/sirApi.h
+++ b/CORE/MAC/inc/sirApi.h
@@ -6437,4 +6437,16 @@
     uint8_t  switch_count;
 };
 
+typedef void (*sir_feature_caps_cb)(void *user_data);
+
+/**
+ * struct sir_feature_caps_params - Feature capability request
+ * @feature_caps_cb: HDD callback to be invoked from WDA
+ * @user_data: associated user-data with feature_caps_cb callback
+ */
+struct sir_feature_caps_params {
+	sir_feature_caps_cb feature_caps_cb;
+	void *user_data;
+};
+
 #endif /* __SIR_API_H */
diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h
index 6be4f82..51c9425 100644
--- a/CORE/SME/inc/sme_Api.h
+++ b/CORE/SME/inc/sme_Api.h
@@ -2808,17 +2808,17 @@
   ---------------------------------------------------------------------------*/
 eHalStatus sme_SetTmLevel(tHalHandle hHal, v_U16_t newTMLevel, v_U16_t tmMode);
 
-/*---------------------------------------------------------------------------
-
-  \brief sme_featureCapsExchange() - SME interface to exchange capabilities between
-  Host and FW.
-
-  \param  hHal - HAL handle for device
-
-  \return NONE
-
----------------------------------------------------------------------------*/
-void sme_featureCapsExchange(tHalHandle hHal);
+/**
+ * sme_featureCapsExchange() - SME API to get firmware feature caps
+ * @params: Pointer to hold HDD callback to be invoked for response
+ * and associated user data.
+ *
+ * This function is used to exchange capabilities between Host and FW.
+ *
+ * Return: VOS_STATUS
+ */
+VOS_STATUS
+sme_featureCapsExchange(struct sir_feature_caps_params *params);
 
 /*---------------------------------------------------------------------------
 
diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c
index 8ced3dc..7b19327 100644
--- a/CORE/SME/src/sme_common/sme_Api.c
+++ b/CORE/SME/src/sme_common/sme_Api.c
@@ -10165,22 +10165,19 @@
     return(status);
 }
 
-/*---------------------------------------------------------------------------
-
-  \brief sme_featureCapsExchange() - SME interface to exchange capabilities between
-  Host and FW.
-
-  \param  hHal - HAL handle for device
-
-  \return NONE
-
----------------------------------------------------------------------------*/
-void sme_featureCapsExchange( tHalHandle hHal)
+VOS_STATUS
+sme_featureCapsExchange(struct sir_feature_caps_params *params)
 {
-    v_CONTEXT_t vosContext = vos_get_global_context(VOS_MODULE_ID_SME, NULL);
-    MTRACE(vos_trace(VOS_MODULE_ID_SME,
-                     TRACE_CODE_SME_RX_HDD_CAPS_EXCH, NO_SESSION, 0));
-    WDA_featureCapsExchange(vosContext);
+	VOS_STATUS status;
+	v_CONTEXT_t vosContext = vos_get_global_context(VOS_MODULE_ID_SME,
+							NULL);
+
+	MTRACE(vos_trace(VOS_MODULE_ID_SME,
+	       TRACE_CODE_SME_RX_HDD_CAPS_EXCH, NO_SESSION, 0));
+
+	status = WDA_featureCapsExchange(vosContext, params);
+
+	return status;
 }
 
 /*---------------------------------------------------------------------------
diff --git a/CORE/WDA/inc/wlan_qct_wda.h b/CORE/WDA/inc/wlan_qct_wda.h
index db75533..c92345e 100644
--- a/CORE/WDA/inc/wlan_qct_wda.h
+++ b/CORE/WDA/inc/wlan_qct_wda.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -2064,28 +2064,17 @@
                  tANI_U32   arg1, tANI_U32   arg2, tANI_U32   arg3,
                  tANI_U32   arg4, tANI_U8   *pBuffer, wpt_boolean async);
 
-/*==========================================================================
-   FUNCTION    WDA_featureCapsExchange
-
-  DESCRIPTION
-    WDA API to invoke capability exchange between host and FW
-
-  DEPENDENCIES
-
-  PARAMETERS
-
-   IN
-    pVosContext         VOS context
-
-   OUT
-    NONE
-
-  RETURN VALUE
-    NONE
-    
-  SIDE EFFECTS
-============================================================================*/
-void WDA_featureCapsExchange(v_PVOID_t pVosContext);
+/**
+ * WDA_featureCapsExchange() - WDA API to invoke capability exchange between
+ * host and FW
+ * @pVosContext: VOS context
+ * @request: Request to hold call-back to be invoked for response
+ *
+ * Return: VOS_STATUS
+ */
+VOS_STATUS
+WDA_featureCapsExchange(v_PVOID_t pVosContext,
+			struct sir_feature_caps_params *request);
 
 void WDA_disableCapablityFeature(tANI_U8 feature_index);
 /*==========================================================================
diff --git a/CORE/WDA/src/wlan_qct_wda.c b/CORE/WDA/src/wlan_qct_wda.c
index 140206c..a18a1ee 100644
--- a/CORE/WDA/src/wlan_qct_wda.c
+++ b/CORE/WDA/src/wlan_qct_wda.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -21295,14 +21295,95 @@
    }
 }
 
- /*  FUNCTION    WDA_featureCapsExchange
-  *  WDA API to invoke capability exchange between host and FW.
-  */
-void WDA_featureCapsExchange(v_PVOID_t pVosContext)
+/**
+ * wda_feature_caps_cb() - Callback to be invoked for feature
+ * capability response received from firmware.
+ * @feat_caps_rsp: feature capability response
+ * @user_data: user input holding HDD callbacks
+ *
+ * Return: None
+ */
+void wda_feature_caps_cb(void *feat_caps_rsp, void *user_data)
 {
-   VOS_TRACE( VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
-      "%s:enter", __func__ );
-   WDI_featureCapsExchangeReq( NULL, pVosContext);
+	tWDA_ReqParams *wda_params = user_data;
+	tWDA_CbContext *wda;
+	struct sir_feature_caps_params *params;
+
+	VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+		  "<------ %s " ,__func__);
+	if(!wda_params) {
+		VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+			  "%s: wda params received NULL", __func__);
+		return;
+	}
+
+	wda = wda_params->pWdaContext;
+	params = wda_params->wdaMsgParam;
+	if(!params) {
+		VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+			  "%s: params received NULL", __func__);
+		goto free_memory;
+	}
+
+	(params->feature_caps_cb)(params->user_data);
+	vos_mem_free(params);
+
+free_memory:
+	vos_mem_free(wda_params->wdaWdiApiMsgParam);
+	vos_mem_free(wda_params);
+}
+
+VOS_STATUS WDA_featureCapsExchange(v_PVOID_t pVosContext,
+				   struct sir_feature_caps_params *request)
+{
+	WDI_Status status;
+	tWDA_CbContext *wda = NULL;
+	tWDA_ReqParams *wda_params = NULL;
+	struct sir_feature_caps_params *params = NULL;
+
+	VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_INFO,
+		  "%s:enter", __func__ );
+
+	wda = (tWDA_CbContext *)vos_get_context(VOS_MODULE_ID_WDA, pVosContext);
+	if(!wda) {
+		VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+			  "%s:pWDA is NULL", __func__);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	wda_params = vos_mem_malloc(sizeof(*wda_params));
+	if(!wda_params) {
+		VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+			  "%s: VOS MEM Alloc Failure", __func__);
+		return VOS_STATUS_E_NOMEM;
+	}
+	vos_mem_zero(wda_params, sizeof(*wda_params));
+
+	if (request) {
+		params = vos_mem_malloc(sizeof(*params));
+		if (!params) {
+			VOS_TRACE(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ERROR,
+				  "%s: VOS MEM Alloc Failure", __func__);
+			vos_mem_free(wda_params);
+			return VOS_STATUS_E_NOMEM;
+		}
+		vos_mem_zero(params, sizeof(*params));
+		*params = *request;
+	}
+
+	wda_params->pWdaContext = wda;
+	wda_params->wdaMsgParam = params;
+	wda_params->wdaWdiApiMsgParam = NULL;
+
+	status = WDI_featureCapsExchangeReq(wda_feature_caps_cb, wda_params);
+	if (status != WDI_STATUS_SUCCESS) {
+		if (params)
+			vos_mem_free(params);
+		vos_mem_free(wda_params);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	return VOS_STATUS_SUCCESS;
 }
 
 /*  FUNCTION    WDA_disableCapablityFeature
diff --git a/CORE/WDI/CP/src/wlan_qct_wdi.c b/CORE/WDI/CP/src/wlan_qct_wdi.c
index e3f78a1..52583af 100644
--- a/CORE/WDI/CP/src/wlan_qct_wdi.c
+++ b/CORE/WDI/CP/src/wlan_qct_wdi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -31049,7 +31049,7 @@
     Send Start Request to HAL 
   -------------------------------------------------------------------------*/
   return  WDI_SendMsg( pWDICtx, pSendBuffer, usSendSize, 
-                       (WDI_StartRspCb)pEventData->pCBfnc,
+                       pEventData->pCBfnc,
                        pEventData->pUserData, WDI_FEATURE_CAPS_EXCHANGE_RESP);
   
 }/*WDI_ProcessFeatureCapsExchangeReq*/
@@ -31124,8 +31124,8 @@
    wdiFeatureCapsExchangeCb = (WDI_featureCapsExchangeCb) pWDICtx -> pfncRspCB; 
 
    /*Notify UMAC - there is no callback right now but can be used in future if reqd */
-   if (wdiFeatureCapsExchangeCb != NULL)
-      wdiFeatureCapsExchangeCb(NULL, NULL);
+   if (wdiFeatureCapsExchangeCb)
+      wdiFeatureCapsExchangeCb(NULL, pWDICtx->pRspCBUserData);
 
    return WDI_STATUS_SUCCESS; 
 }