qcacld-3.0: Add support for NDP vendor commands and NDI Create

Add support for NDP vendor commands and implement NAN Data Interface
create.

Propagation from qcacld-2.0 to qcacld-3.0

CRs-Fixed: 962367
Change-Id: I84e9ac5ccfe8faaa00dfc448defb81fb792263d5
diff --git a/core/hdd/src/wlan_hdd_nan_datapath.c b/core/hdd/src/wlan_hdd_nan_datapath.c
index eac5305..71fbce1 100644
--- a/core/hdd/src/wlan_hdd_nan_datapath.c
+++ b/core/hdd/src/wlan_hdd_nan_datapath.c
@@ -23,8 +23,39 @@
  *
  * WLAN Host Device Driver nan datapath API implementation
  */
-#include <wlan_hdd_includes.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include "wlan_hdd_includes.h"
 #include "wlan_hdd_nan_datapath.h"
+#include "wlan_hdd_p2p.h"
+#include "wma_api.h"
+
+/* NLA policy */
+static const struct nla_policy
+qca_wlan_vendor_ndp_policy[QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX + 1] = {
+	[QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID] = { .type = NLA_U16 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR] = { .type = NLA_STRING,
+					.len = IFNAMSIZ },
+	[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_SPEC_CHANNEL] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR] = {
+						.type = NLA_BINARY,
+						.len = QDF_MAC_ADDR_SIZE },
+	[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY] = { .type = NLA_U16 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS] = { .type = NLA_BINARY,
+					.len = NDP_QOS_INFO_LEN },
+	[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO_LEN] = { .type = NLA_U16 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO] = { .type = NLA_BINARY,
+					.len = NDP_APP_INFO_LEN },
+	[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_RESPONSE_CODE] = { .type = NLA_U16 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_STATUS_CODE] = { .type = NLA_U16 },
+	[QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR] = { .type = NLA_BINARY,
+					.len = QDF_MAC_ADDR_SIZE },
+};
 
 /**
  * hdd_ndp_print_ini_config()- Print nan datapath specific INI configuration
@@ -47,12 +78,11 @@
  * @hdd_ctx: Pointer to HDD context
  * @cfg: Pointer to target device capability information
  *
- * NaN datapath functionality is enabled if it is enabled in
- * .ini file and also supported in target device.
+ * NAN datapath functinality is enabled if it is enabled in
+ * .ini file and also supported in firmware.
  *
  * Return: None
  */
-
 void hdd_nan_datapath_target_config(hdd_context_t *hdd_ctx,
 					struct wma_tgt_cfg *cfg)
 {
@@ -60,3 +90,770 @@
 	hddLog(LOG1, FL("enable_nan_datapath: %d"),
 		hdd_ctx->config->enable_nan_datapath);
 }
+
+/**
+ * hdd_ndi_start_bss() - Start BSS on NAN data interface
+ * @adapter: adapter context
+ * @operating_channel: channel on which the BSS to be started
+ *
+ * Return: 0 on success, error value on failure
+ */
+static int hdd_ndi_start_bss(hdd_adapter_t *adapter,
+				uint8_t operating_channel)
+{
+	int ret;
+	uint32_t roam_id;
+	hdd_wext_state_t *wext_state =
+		WLAN_HDD_GET_NDP_WEXT_STATE_PTR(adapter);
+	tCsrRoamProfile *roam_profile = &wext_state->roamProfile;
+
+	ENTER();
+
+	if (!roam_profile) {
+		hddLog(LOGE, FL("No valid roam profile"));
+		return -EINVAL;
+	}
+
+	if (HDD_WMM_USER_MODE_NO_QOS ==
+		(WLAN_HDD_GET_CTX(adapter))->config->WmmMode) {
+		/* QoS not enabled in cfg file*/
+		roam_profile->uapsd_mask = 0;
+	} else {
+		/* QoS enabled, update uapsd mask from cfg file*/
+		roam_profile->uapsd_mask =
+			(WLAN_HDD_GET_CTX(adapter))->config->UapsdMask;
+	}
+
+	roam_profile->csrPersona = adapter->device_mode;
+
+	roam_profile->ChannelInfo.numOfChannels = 1;
+	if (operating_channel) {
+		roam_profile->ChannelInfo.ChannelList = &operating_channel;
+	} else {
+		roam_profile->ChannelInfo.ChannelList[0] =
+			NAN_SOCIAL_CHANNEL_2_4GHZ;
+	}
+	hdd_select_cbmode(adapter, operating_channel);
+
+	roam_profile->SSIDs.numOfSSIDs = 1;
+	roam_profile->SSIDs.SSIDList->SSID.length = 0;
+
+	roam_profile->phyMode = eCSR_DOT11_MODE_11ac;
+	roam_profile->BSSType = eCSR_BSS_TYPE_NDI;
+	roam_profile->BSSIDs.numOfBSSIDs = 1;
+	qdf_mem_copy((void *)(roam_profile->BSSIDs.bssid),
+		&adapter->macAddressCurrent.bytes[0],
+		QDF_MAC_ADDR_SIZE);
+
+	roam_profile->AuthType.numEntries = 1;
+	roam_profile->AuthType.authType[0] = eCSR_AUTH_TYPE_OPEN_SYSTEM;
+	roam_profile->EncryptionType.numEntries = 1;
+	roam_profile->EncryptionType.encryptionType[0] = eCSR_ENCRYPT_TYPE_NONE;
+
+	ret = sme_roam_connect(WLAN_HDD_GET_HAL_CTX(adapter),
+		adapter->sessionId, roam_profile, &roam_id);
+	if (QDF_STATUS_SUCCESS != ret) {
+		hddLog(LOGE,
+			FL("NDI sme_RoamConnect session %d failed with status %d -> NotConnected"),
+			  adapter->sessionId, ret);
+		/* change back to NotConnected */
+		hdd_conn_set_connection_state(adapter,
+			eConnectionState_NotConnected);
+	} else {
+		hddLog(LOG2, FL("sme_RoamConnect issued successfully for NDI"));
+	}
+
+	roam_profile->ChannelInfo.ChannelList = NULL;
+	roam_profile->ChannelInfo.numOfChannels = 0;
+
+	EXIT();
+
+	return ret;
+}
+
+
+/**
+ * hdd_ndi_create_req_handler() - NDI create request handler
+ * @hdd_ctx: hdd context
+ * @tb: parsed NL attribute list
+ *
+ * Return: 0 on success or error code on failure
+ */
+static int hdd_ndi_create_req_handler(hdd_context_t *hdd_ctx,
+						struct nlattr **tb)
+{
+	hdd_adapter_t *adapter;
+	char *iface_name;
+	uint16_t transaction_id;
+	int ret;
+	struct nan_datapath_ctx *ndp_ctx;
+	uint8_t op_channel =
+		hdd_ctx->config->nan_datapath_ndi_channel;
+
+	ENTER();
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]) {
+		hddLog(LOGE, FL("Interface name string is unavailable"));
+		return -EINVAL;
+	}
+	iface_name = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]);
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) {
+		hddLog(LOGE, FL("transaction id is unavailable"));
+		return -EINVAL;
+	}
+	transaction_id =
+		nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]);
+
+	/* Check for an existing interface of NDI type */
+	adapter = hdd_get_adapter(hdd_ctx, QDF_NDI_MODE);
+	if (adapter) {
+		hddLog(LOGE, FL("Cannot support more than one NDI"));
+		return -EEXIST;
+	}
+
+	adapter = hdd_open_adapter(hdd_ctx, QDF_NDI_MODE, iface_name,
+			wlan_hdd_get_intf_addr(hdd_ctx), NET_NAME_UNKNOWN,
+			true);
+	if (!adapter) {
+		hddLog(LOGE, FL("hdd_open_adapter failed"));
+		return -ENOMEM;
+	}
+
+	/*
+	 * Create transaction id is required to be saved since the firmware
+	 * does not honor the transaction id for create request
+	 */
+	ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
+	ndp_ctx->ndp_create_transaction_id = transaction_id;
+
+	/*
+	 * The NAN data interface has been created at this point.
+	 * Unlike traditional device modes, where the higher application
+	 * layer initiates connect / join / start, the NAN data interface
+	 * does not have any such formal requests. The NDI create request
+	 * is responsible for starting the BSS as well.
+	 */
+	if (op_channel != NAN_SOCIAL_CHANNEL_2_4GHZ ||
+	    op_channel != NAN_SOCIAL_CHANNEL_5GHZ_LOWER_BAND ||
+	    op_channel != NAN_SOCIAL_CHANNEL_5GHZ_UPPER_BAND) {
+		/* start NDI on the default 2.4 GHz social channel */
+		op_channel = NAN_SOCIAL_CHANNEL_2_4GHZ;
+	}
+	ret = hdd_ndi_start_bss(adapter, op_channel);
+	if (0 > ret)
+		hddLog(LOGE, FL("NDI start bss failed"));
+
+	EXIT();
+	return ret;
+}
+
+/**
+ * hdd_ndi_delete_req_handler() - NDI delete request handler
+ * @hdd_ctx: hdd context
+ * @tb: parsed NL attribute list
+ *
+ * Return: 0 on success or error code on failure
+ */
+static int hdd_ndi_delete_req_handler(hdd_context_t *hdd_ctx,
+						struct nlattr **tb)
+{
+	return 0;
+}
+
+
+/**
+ * hdd_ndp_initiator_req_handler() - NDP initiator request handler
+ * @hdd_ctx: hdd context
+ * @tb: parsed NL attribute list
+ *
+ * Return:  0 on success or error code on failure
+ */
+static int hdd_ndp_initiator_req_handler(hdd_context_t *hdd_ctx,
+						struct nlattr **tb)
+{
+	return 0;
+}
+
+/**
+ * hdd_ndp_responder_req_handler() - NDP responder request handler
+ * @hdd_ctx: hdd context
+ * @tb: parsed NL attribute list
+ *
+ * Return: 0 on success or error code on failure
+ */
+static int hdd_ndp_responder_req_handler(hdd_context_t *hdd_ctx,
+						struct nlattr **tb)
+{
+	return 0;
+}
+
+/**
+ * hdd_ndp_end_req_handler() - NDP end request handler
+ * @hdd_ctx: hdd context
+ * @tb: parsed NL attribute list
+ *
+ * Return: 0 on success or error code on failure
+ */
+static int hdd_ndp_end_req_handler(hdd_context_t *hdd_ctx,
+						struct nlattr **tb)
+{
+	return 0;
+}
+
+/**
+ * hdd_ndp_schedule_req_handler() - NDP schedule request handler
+ * @hdd_ctx: hdd context
+ * @tb: parsed NL attribute list
+ *
+ * Return: 0 on success or error code on failure
+ */
+static int hdd_ndp_schedule_req_handler(hdd_context_t *hdd_ctx,
+						struct nlattr **tb)
+{
+	return 0;
+}
+
+
+/**
+ * hdd_ndp_iface_create_rsp_handler() - NDP iface create response handler
+ * @adapter: pointer to adapter context
+ * @rsp_params: response parameters
+ *
+ * The function is expected to send a response back to the user space
+ * even if the creation of BSS has failed
+ *
+ * Following vendor event is sent to cfg80211:
+ * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
+ * QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE
+ *
+ * Return: none
+ */
+static void hdd_ndp_iface_create_rsp_handler(hdd_adapter_t *adapter,
+							void *rsp_params)
+{
+	struct sk_buff *vendor_event;
+	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	struct ndi_create_rsp *ndi_rsp = (struct ndi_create_rsp *)rsp_params;
+	uint32_t data_len = (3 * sizeof(uint32_t)) + sizeof(uint16_t) +
+				NLMSG_HDRLEN + (4 * NLA_HDRLEN);
+	struct nan_datapath_ctx *ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
+
+	ENTER();
+
+	if (wlan_hdd_validate_context(hdd_ctx))
+		return;
+
+	if (!ndi_rsp) {
+		hddLog(LOGE, FL("Invalid ndi create response"));
+		return;
+	}
+
+	/* notify response to the upper layer */
+	vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
+				NULL,
+				data_len,
+				QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX,
+				cds_get_gfp_flags());
+
+	if (!vendor_event) {
+		hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
+		return;
+	}
+
+	/* Sub vendor command */
+	if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD,
+		QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE)) {
+		hddLog(LOGE, FL("QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD put fail"));
+		goto nla_put_failure;
+	}
+
+	/* Transaction id */
+	if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
+		ndp_ctx->ndp_create_transaction_id)) {
+		hddLog(LOGE, FL("VENDOR_ATTR_NDP_TRANSACTION_ID put fail"));
+		goto nla_put_failure;
+	}
+
+	/* Status code */
+	if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE,
+		ndi_rsp->status)) {
+		hddLog(LOGE, FL("VENDOR_ATTR_NDP_DRV_RETURN_TYPE put fail"));
+		goto nla_put_failure;
+	}
+
+	/* Status return value */
+	if (nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, 0xA5)) {
+		hddLog(LOGE, FL("VENDOR_ATTR_NDP_DRV_RETURN_VALUE put fail"));
+		goto nla_put_failure;
+	}
+
+	hddLog(LOG2, FL("sub command: %d, value: %d"),
+		QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX,
+		QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE);
+	hddLog(LOG2, FL("create transaction id: %d, value: %d"),
+		QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
+		ndp_ctx->ndp_create_transaction_id);
+	hddLog(LOG2, FL("status code: %d, value: %d"),
+		QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE, ndi_rsp->status);
+	hddLog(LOG2, FL("Return value: %d, value: %d"),
+		QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, 0xA5);
+
+	cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+
+	ndp_ctx->ndp_create_transaction_id = 0;
+
+	if (ndi_rsp->status == QDF_STATUS_SUCCESS) {
+		hddLog(LOGE, FL("NDI interface successfully created"));
+	} else {
+		hddLog(LOGE,
+			FL("NDI interface creation failed with reason %d"),
+			ndi_rsp->reason);
+	}
+	EXIT();
+	return;
+
+nla_put_failure:
+	kfree_skb(vendor_event);
+	return;
+}
+
+/**
+ * hdd_ndp_iface_delete_rsp_handler() - NDP iface delete response handler
+ * @adapter: pointer to adapter context
+ * @rsp_params: response parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_iface_delete_rsp_handler(hdd_adapter_t *adapter,
+							void *rsp_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_initiator_rsp_handler() - NDP initiator response handler
+ * @adapter: pointer to adapter context
+ * @rsp_params: response parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_initiator_rsp_handler(hdd_adapter_t *adapter,
+						void *rsp_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_new_peer_ind_handler() - NDP new peer indication handler
+ * @adapter: pointer to adapter context
+ * @ind_params: indication parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_new_peer_ind_handler(hdd_adapter_t *adapter,
+						void *ind_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_peer_departed_ind_handler() - NDP peer departed indication handler
+ * @adapter: pointer to adapter context
+ * @ind_params: indication parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_peer_departed_ind_handler(
+				hdd_adapter_t *adapter, void *ind_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_confirm_ind_handler() - NDP confirm indication handler
+ * @adapter: pointer to adapter context
+ * @ind_params: indication parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_confirm_ind_handler(hdd_adapter_t *adapter,
+						void *ind_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_indication_handler() - NDP indication handler
+ * @adapter: pointer to adapter context
+ * @ind_params: indication parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_indication_handler(hdd_adapter_t *adapter,
+						void *ind_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_responder_rsp_handler() - NDP responder response handler
+ * @adapter: pointer to adapter context
+ * @rsp_params: response parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_responder_rsp_handler(hdd_adapter_t *adapter,
+							void *rsp_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_end_rsp_handler() - NDP end response handler
+ * @adapter: pointer to adapter context
+ * @rsp_params: response parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_end_rsp_handler(hdd_adapter_t *adapter,
+						void *rsp_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_end_ind_handler() - NDP end indication handler
+ * @adapter: pointer to adapter context
+ * @ind_params: indication parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_end_ind_handler(hdd_adapter_t *adapter,
+						void *ind_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_schedule_update_rsp_handler() - NDP schedule update response handler
+ * @adapter: pointer to adapter context
+ * @rsp_params: response parameters
+ *
+ * Return: none
+ */
+static void hdd_ndp_schedule_update_rsp_handler(
+				hdd_adapter_t *adapter, void *rsp_params)
+{
+	return;
+}
+
+/**
+ * hdd_ndp_event_handler() - ndp response and indication handler
+ * @adapter: adapter context
+ * @roam_info: pointer to roam_info structure
+ * @roam_id: roam id as indicated by SME
+ * @roam_status: roam status
+ * @roam_result: roam result
+ *
+ * Return: none
+ */
+void hdd_ndp_event_handler(hdd_adapter_t *adapter,
+	tCsrRoamInfo *roam_info, uint32_t roam_id, eRoamCmdStatus roam_status,
+	eCsrRoamResult roam_result)
+{
+	if (roam_status == eCSR_ROAM_NDP_STATUS_UPDATE) {
+		switch (roam_result) {
+		case eCSR_ROAM_RESULT_NDP_CREATE_RSP:
+			hdd_ndp_iface_create_rsp_handler(adapter,
+				&roam_info->ndp.ndi_create_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_DELETE_RSP:
+			hdd_ndp_iface_delete_rsp_handler(adapter,
+				&roam_info->ndp.ndi_delete_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_INITIATOR_RSP:
+			hdd_ndp_initiator_rsp_handler(adapter,
+				&roam_info->ndp.ndp_init_rsp_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_NEW_PEER_IND:
+			hdd_ndp_new_peer_ind_handler(adapter,
+				&roam_info->ndp.ndp_peer_ind_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_CONFIRM_IND:
+			hdd_ndp_confirm_ind_handler(adapter,
+				&roam_info->ndp.ndp_confirm_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_INDICATION:
+			hdd_ndp_indication_handler(adapter,
+				&roam_info->ndp.ndp_indication_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_SCHED_UPDATE_RSP:
+			hdd_ndp_schedule_update_rsp_handler(adapter,
+				&roam_info->ndp.ndp_sched_upd_rsp_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_RESPONDER_RSP:
+			hdd_ndp_responder_rsp_handler(adapter,
+				&roam_info->ndp.ndp_responder_rsp_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_END_RSP:
+			hdd_ndp_end_rsp_handler(adapter,
+				&roam_info->ndp.ndp_end_rsp_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_PEER_DEPARTED_IND:
+			hdd_ndp_peer_departed_ind_handler(adapter,
+				&roam_info->ndp.ndp_peer_ind_params);
+			break;
+		case eCSR_ROAM_RESULT_NDP_END_IND:
+			hdd_ndp_end_ind_handler(adapter,
+				&roam_info->ndp.ndp_end_ind_params);
+			break;
+		default:
+			hddLog(LOGE,
+				FL("Unknown NDP response event from SME %d"),
+				roam_result);
+			break;
+		}
+	}
+}
+
+/**
+ * __wlan_hdd_cfg80211_process_ndp_cmds() - handle NDP request
+ * @wiphy: pointer to wireless wiphy structure.
+ * @wdev: pointer to wireless_dev structure.
+ * @data: Pointer to the data to be passed via vendor interface
+ * @data_len:Length of the data to be passed
+ *
+ * This function is invoked to handle vendor command
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int __wlan_hdd_cfg80211_process_ndp_cmd(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void *data, int data_len)
+{
+	uint32_t ndp_cmd_type;
+	uint16_t transaction_id;
+	int ret_val;
+	hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX + 1];
+	char *iface_name;
+
+	ENTER();
+
+	ret_val = wlan_hdd_validate_context(hdd_ctx);
+	if (ret_val)
+		return ret_val;
+
+	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
+		hddLog(LOGE, FL("Command not allowed in FTM mode"));
+		return -EPERM;
+	}
+	if (!hdd_ctx->config->enable_nan_datapath) {
+		hddLog(LOGE, FL("NAN datapath is not suported"));
+		return -EPERM;
+	}
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX,
+			data, data_len,
+			qca_wlan_vendor_ndp_policy)) {
+		hddLog(LOGE, FL("Invalid NDP vendor command attributes"));
+		return -EINVAL;
+	}
+
+	/* Parse and fetch NDP Command Type*/
+	if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD]) {
+		hddLog(LOGE, FL("NAN datapath cmd type failed"));
+		return -EINVAL;
+	}
+	ndp_cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD]);
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) {
+		hddLog(LOGE, FL("attr transaction id failed"));
+		return -EINVAL;
+	}
+	transaction_id = nla_get_u16(
+			tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]);
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]) {
+		hddLog(LOGE, FL("Interface name string is unavailable"));
+		return -EINVAL;
+	}
+	iface_name = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]);
+
+	hddLog(LOG2, FL("Transaction Id: %d NDP Cmd: %d iface_name: %s"),
+		transaction_id, ndp_cmd_type, iface_name);
+
+	switch (ndp_cmd_type) {
+	case QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE:
+		ret_val  = hdd_ndi_create_req_handler(hdd_ctx, tb);
+		break;
+	case QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE:
+		ret_val = hdd_ndi_delete_req_handler(hdd_ctx, tb);
+		break;
+	case QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST:
+		ret_val = hdd_ndp_initiator_req_handler(hdd_ctx, tb);
+		break;
+	case QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST:
+		ret_val = hdd_ndp_responder_req_handler(hdd_ctx, tb);
+		break;
+	case QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST:
+		ret_val = hdd_ndp_end_req_handler(hdd_ctx, tb);
+		break;
+	case QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REQUEST:
+		ret_val = hdd_ndp_schedule_req_handler(hdd_ctx, tb);
+		break;
+	default:
+		hddLog(LOGE, FL("Unrecognized NDP vendor cmd %d"),
+			ndp_cmd_type);
+		ret_val = -EINVAL;
+		break;
+	}
+
+	return ret_val;
+}
+
+/**
+ * wlan_hdd_cfg80211_process_ndp_cmd() - handle NDP request
+ * @wiphy: pointer to wireless wiphy structure.
+ * @wdev: pointer to wireless_dev structure.
+ * @data: Pointer to the data to be passed via vendor interface
+ * @data_len:Length of the data to be passed
+ *
+ * This function is called to send a NAN request to
+ * firmware. This is an SSR-protected wrapper function.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int wlan_hdd_cfg80211_process_ndp_cmd(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void *data, int data_len)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_cfg80211_process_ndp_cmd(wiphy, wdev, data, data_len);
+	cds_ssr_unprotect(__func__);
+
+	return ret;
+}
+
+/**
+ * hdd_init_nan_data_mode() - initialize nan data mode
+ * @adapter: adapter context
+ *
+ * Returns: 0 on success negative error code on error
+ */
+int hdd_init_nan_data_mode(struct hdd_adapter_s *adapter)
+{
+	struct net_device *wlan_dev = adapter->dev;
+	struct nan_datapath_ctx *ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
+	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	QDF_STATUS status;
+	uint32_t type, sub_type;
+	int32_t ret_val = 0;
+	unsigned long rc;
+	uint32_t timeout = WLAN_WAIT_TIME_SESSIONOPENCLOSE;
+
+	INIT_COMPLETION(adapter->session_open_comp_var);
+	sme_set_curr_device_mode(hdd_ctx->hHal, adapter->device_mode);
+	status = cds_get_vdev_types(adapter->device_mode, &type, &sub_type);
+	if (QDF_STATUS_SUCCESS != status) {
+		hddLog(LOGE, "failed to get vdev type");
+		goto error_sme_open;
+	}
+
+	/* open sme session for future use */
+	status = sme_open_session(hdd_ctx->hHal, hdd_sme_roam_callback,
+			adapter, (uint8_t *)&adapter->macAddressCurrent,
+			&adapter->sessionId, type, sub_type);
+	if (QDF_STATUS_SUCCESS == status) {
+		hddLog(LOGE, "sme_open_session() failed with status code %d",
+				status);
+		ret_val = -EAGAIN;
+		goto error_sme_open;
+	}
+
+	/* Block on a completion variable. Can't wait forever though */
+	rc = wait_for_completion_timeout(
+			&adapter->session_open_comp_var,
+			msecs_to_jiffies(timeout));
+	if (!rc) {
+		hddLog(LOGE,
+			FL("Failed to open session, timeout code: %ld"), rc);
+		ret_val = -ETIMEDOUT;
+		goto error_sme_open;
+	}
+
+	/* Register wireless extensions */
+	ret_val = hdd_register_wext(wlan_dev);
+	if (0 > ret_val) {
+		hddLog(LOGE, FL("Wext registration failed with status code %d"),
+				ret_val);
+		ret_val = -EAGAIN;
+		goto error_register_wext;
+	}
+
+	status = hdd_init_tx_rx(adapter);
+	if (QDF_STATUS_SUCCESS != status) {
+		hddLog(LOGE, FL("hdd_init_tx_rx() init failed, status %d"),
+				status);
+		ret_val = -EAGAIN;
+		goto error_init_txrx;
+	}
+
+	set_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags);
+
+	status = hdd_wmm_adapter_init(adapter);
+	if (QDF_STATUS_SUCCESS != status) {
+		hddLog(LOGE, FL("hdd_wmm_adapter_init() failed, status %d"),
+				status);
+		ret_val = -EAGAIN;
+		goto error_wmm_init;
+	}
+
+	set_bit(WMM_INIT_DONE, &adapter->event_flags);
+
+	ret_val = wma_cli_set_command((int)adapter->sessionId,
+			(int)WMI_PDEV_PARAM_BURST_ENABLE,
+			(int)hdd_ctx->config->enableSifsBurst,
+			PDEV_CMD);
+	if (0 != ret_val) {
+		hddLog(LOGE, FL("WMI_PDEV_PARAM_BURST_ENABLE set failed %d"),
+				ret_val);
+	}
+
+	ndp_ctx->state = NAN_DATA_NDI_CREATING_STATE;
+	return ret_val;
+
+error_wmm_init:
+	clear_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags);
+	hdd_deinit_tx_rx(adapter);
+
+error_init_txrx:
+	hdd_unregister_wext(wlan_dev);
+
+error_register_wext:
+	if (test_bit(SME_SESSION_OPENED, &adapter->event_flags)) {
+		INIT_COMPLETION(adapter->session_close_comp_var);
+		if (QDF_STATUS_SUCCESS ==
+				sme_close_session(hdd_ctx->hHal,
+					adapter->sessionId,
+					hdd_sme_close_session_callback,
+					adapter)) {
+			rc = wait_for_completion_timeout(
+					&adapter->session_close_comp_var,
+					msecs_to_jiffies(timeout));
+			if (rc <= 0) {
+				hddLog(LOGE,
+					FL("Session close failed status %ld"),
+					rc);
+				ret_val = -ETIMEDOUT;
+			}
+		}
+	}
+
+error_sme_open:
+	return ret_val;
+}