/*
 * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/**
 * DOC: wma_nan_datapath.c
 *
 * WMA NAN Data path API implementation
 */

#include "wma.h"
#include "wma_api.h"
#include "vos_api.h"
#include "wmi_unified_api.h"
#include "wmi_unified.h"
#include "wma_nan_datapath.h"

/**
 * wma_handle_ndp_initiator_req() - NDP initiator request handler
 * @wma_handle: wma handle
 * @req: request parameters
 *
 * Return: VOS_STATUS_SUCCESS on success; error number otherwise
 */
VOS_STATUS wma_handle_ndp_initiator_req(tp_wma_handle wma_handle, void *req)
{
	VOS_STATUS status;
	int ret;
	uint16_t len;
	uint32_t vdev_id, ndp_cfg_len, ndp_app_info_len, pmk_len;
	struct ndp_initiator_rsp *rsp = NULL;
	ol_txrx_vdev_handle vdev;
	wmi_buf_t buf;
	wmi_ndp_initiator_req_fixed_param *cmd;
	vos_msg_t pe_msg = {0};
	struct ndp_initiator_req *ndp_req = req;
	wmi_channel *ch_tlv;
	uint8_t *tlv_ptr;

	if (NULL == ndp_req) {
		WMA_LOGE(FL("Invalid ndp_req."));
		goto send_ndp_initiator_fail;
	}
	vdev_id = ndp_req->vdev_id;
	vdev = wma_find_vdev_by_id(wma_handle, vdev_id);
	if (!vdev) {
		WMA_LOGE(FL("vdev not found for vdev id %d."), vdev_id);
		goto send_ndp_initiator_fail;
	}

	if (!WMA_IS_VDEV_IN_NDI_MODE(wma_handle->interfaces, vdev_id)) {
		WMA_LOGE(FL("vdev :%d, not in NDI mode"), vdev_id);
		goto send_ndp_initiator_fail;
	}

	/*
	 * WMI command expects 4 byte alligned len:
	 * round up ndp_cfg_len and ndp_app_info_len to 4 bytes
	 */
	ndp_cfg_len = vos_roundup(ndp_req->ndp_config.ndp_cfg_len, 4);
	ndp_app_info_len = vos_roundup(ndp_req->ndp_info.ndp_app_info_len, 4);
	pmk_len = vos_roundup(ndp_req->pmk.pmk_len, 4);
	/* allocated memory for fixed params as well as variable size data */
	len = sizeof(*cmd) + sizeof(*ch_tlv) + (3 * WMI_TLV_HDR_SIZE)
		+ ndp_cfg_len + ndp_app_info_len + pmk_len;

	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		goto send_ndp_initiator_fail;
	}
	cmd = (wmi_ndp_initiator_req_fixed_param *) wmi_buf_data(buf);
	WMITLV_SET_HDR(&cmd->tlv_header,
		       WMITLV_TAG_STRUC_wmi_ndp_initiator_req_fixed_param,
		       WMITLV_GET_STRUCT_TLVLEN(
				wmi_ndp_initiator_req_fixed_param));
	cmd->vdev_id = ndp_req->vdev_id;
	cmd->transaction_id = ndp_req->transaction_id;
	cmd->service_instance_id = ndp_req->service_instance_id;
	WMI_CHAR_ARRAY_TO_MAC_ADDR(ndp_req->peer_discovery_mac_addr.bytes,
				   &cmd->peer_discovery_mac_addr);

	cmd->ndp_cfg_len = ndp_req->ndp_config.ndp_cfg_len;
	cmd->ndp_app_info_len = ndp_req->ndp_info.ndp_app_info_len;
	cmd->ndp_channel_cfg = ndp_req->channel_cfg;
	cmd->nan_pmk_len = ndp_req->pmk.pmk_len;
	cmd->nan_csid = ndp_req->ncs_sk_type;

	ch_tlv = (wmi_channel *)&cmd[1];
	WMITLV_SET_HDR(ch_tlv, WMITLV_TAG_STRUC_wmi_channel,
			WMITLV_GET_STRUCT_TLVLEN(wmi_channel));
	ch_tlv->mhz = ndp_req->channel;
	ch_tlv->band_center_freq1 =
		vos_chan_to_freq(vos_freq_to_chan(ndp_req->channel));
	tlv_ptr = (uint8_t *)&ch_tlv[1];

	WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_cfg_len);
	vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
		     ndp_req->ndp_config.ndp_cfg, cmd->ndp_cfg_len);
	tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_cfg_len;

	WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_app_info_len);
	vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
		     ndp_req->ndp_info.ndp_app_info, cmd->ndp_app_info_len);
	tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_app_info_len;

	WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, pmk_len);
	vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE], ndp_req->pmk.pmk,
		     cmd->nan_pmk_len);
	tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + pmk_len;

	WMA_LOGE(FL("vdev_id = %d, transaction_id: %d, service_instance_id: %d, ch: %d, ch_cfg: %d, csid: %d"),
		cmd->vdev_id, cmd->transaction_id, cmd->service_instance_id,
		ch_tlv->mhz, cmd->ndp_channel_cfg, cmd->nan_csid);
	WMA_LOGE(FL("peer mac addr: mac_addr31to0: 0x%x, mac_addr47to32: 0x%x"),
		cmd->peer_discovery_mac_addr.mac_addr31to0,
		cmd->peer_discovery_mac_addr.mac_addr47to32);

	WMA_LOGE(FL("ndp_config len: %d"), cmd->ndp_cfg_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			   ndp_req->ndp_config.ndp_cfg,
			   ndp_req->ndp_config.ndp_cfg_len);

	WMA_LOGE(FL("ndp_app_info len: %d"), cmd->ndp_app_info_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			   ndp_req->ndp_info.ndp_app_info,
			   ndp_req->ndp_info.ndp_app_info_len);

	WMA_LOGE(FL("pmk len: %d"), cmd->nan_pmk_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			   ndp_req->pmk.pmk, cmd->nan_pmk_len);
	WMA_LOGE(FL("sending WMI_NDP_INITIATOR_REQ_CMDID(0x%X)"),
		WMI_NDP_INITIATOR_REQ_CMDID);

	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_NDP_INITIATOR_REQ_CMDID);
	if (ret < 0) {
		WMA_LOGE(FL("WMI_NDP_INITIATOR_REQ_CMDID failed, ret: %d"),
			ret);
		wmi_buf_free(buf);
		goto send_ndp_initiator_fail;
	}

	return VOS_STATUS_SUCCESS;

send_ndp_initiator_fail:
	status = VOS_STATUS_E_FAILURE;
	if (ndp_req) {
		rsp = vos_mem_malloc(sizeof(*rsp));
		if (NULL == rsp) {
			WMA_LOGE(FL("Memory allocation failure"));
			status = VOS_STATUS_E_NOMEM;
			/* unblock SME queue, but do not send rsp to HDD */
			pe_msg.bodyval = true;
		} else {
			rsp->vdev_id = ndp_req->vdev_id;
			rsp->transaction_id = ndp_req->transaction_id;
			rsp->ndp_instance_id = ndp_req->service_instance_id;
			rsp->status = NDP_RSP_STATUS_ERROR;
			rsp->reason = NDP_DATA_INITIATOR_REQ_FAILED;
		}
	} else {
		/* unblock SME queue, but do not send rsp to HDD */
		pe_msg.bodyval = true;
	}

	pe_msg.type = SIR_HAL_NDP_INITIATOR_RSP;
	pe_msg.bodyptr = rsp;

	if (VOS_STATUS_SUCCESS !=
			vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg)) {
		WMA_LOGE("SIR_HAL_NDP_INITIATOR_RSP to PE failed");
		vos_mem_free(rsp);
	}

	return status;
}

/**
 * wma_send_ndp_responder_rsp() - NDP responder request handler
 * @rsp_ind: rsp parameters
 *
 * Return: VOS_STATUS_SUCCESS on success; error number otherwise
 */
static VOS_STATUS wma_send_ndp_responder_rsp
	(struct ndp_responder_rsp_event *rsp_ind)
{
	VOS_STATUS status;
	vos_msg_t pe_msg = {0};
	uint8_t len = sizeof(*rsp_ind);

	if (NULL == rsp_ind) {
		WMA_LOGE(FL("Invalid rsp_ind"));
		/* msg to unblock SME, but not send rsp to HDD */
		pe_msg.bodyptr = NULL;
		pe_msg.bodyval = true;
		goto send_rsp;
	}
	pe_msg.bodyptr = vos_mem_malloc(len);
	if (NULL == pe_msg.bodyptr) {
		WMA_LOGE(FL("Memory allocation failure"));
		/* msg to unblock SME, but not send rsp to HDD */
		pe_msg.bodyval = true;
	} else {
		vos_mem_copy(pe_msg.bodyptr, rsp_ind, len);
		pe_msg.bodyval = 0;
	}
send_rsp:
	pe_msg.type = SIR_HAL_NDP_RESPONDER_RSP;
	status = vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		WMA_LOGE(FL("fail to post msg to PE"));
		vos_mem_free(pe_msg.bodyptr);
	}
	return status;
}

/**
 * wma_handle_ndp_responder_req() - NDP responder request handler
 * @wma_handle: wma handle
 * @req_params: request parameters
 *
 * Return: VOS_STATUS_SUCCESS on success; error number otherwise
 */
VOS_STATUS wma_handle_ndp_responder_req(tp_wma_handle wma_handle,
					struct ndp_responder_req *req_params)
{
	wmi_buf_t buf;
	ol_txrx_vdev_handle vdev;
	uint32_t vdev_id = 0, ndp_cfg_len, ndp_app_info_len, pmk_len;
	uint8_t *tlv_ptr;
	int ret;
	wmi_ndp_responder_req_fixed_param *cmd;
	uint16_t len;
	struct ndp_responder_rsp_event rsp;

	if (NULL == req_params) {
		WMA_LOGE(FL("Invalid req_params."));
		return VOS_STATUS_E_INVAL;
	}

	vdev_id = req_params->vdev_id;
	WMA_LOGD(FL("vdev_id: %d, transaction_id: %d, ndp_rsp %d, ndp_instance_id: %d, ndp_app_info_len: %d"),
			req_params->vdev_id, req_params->transaction_id,
			req_params->ndp_rsp,
			req_params->ndp_instance_id,
			req_params->ndp_info.ndp_app_info_len);
	vdev = wma_find_vdev_by_id(wma_handle, vdev_id);
	if (!vdev) {
		WMA_LOGE(FL("vdev not found for vdev id %d."), vdev_id);
		goto send_ndp_responder_fail;
	}

	if (!WMA_IS_VDEV_IN_NDI_MODE(wma_handle->interfaces, vdev_id)) {
		WMA_LOGE(FL("vdev :$%d, not in NDI mode"), vdev_id);
		goto send_ndp_responder_fail;
	}

	/*
	 * WMI command expects 4 byte alligned len:
	 * round up ndp_cfg_len and ndp_app_info_len to 4 bytes
	 */
	ndp_cfg_len = vos_roundup(req_params->ndp_config.ndp_cfg_len, 4);
	ndp_app_info_len = vos_roundup(req_params->ndp_info.ndp_app_info_len, 4);
	pmk_len = vos_roundup(req_params->pmk.pmk_len, 4);
	/* allocated memory for fixed params as well as variable size data */
	len = sizeof(*cmd) + 3*WMI_TLV_HDR_SIZE + ndp_cfg_len + ndp_app_info_len
		+ pmk_len;

	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		goto send_ndp_responder_fail;
	}
	cmd = (wmi_ndp_responder_req_fixed_param *) wmi_buf_data(buf);
	WMITLV_SET_HDR(&cmd->tlv_header,
			WMITLV_TAG_STRUC_wmi_ndp_responder_req_fixed_param,
			WMITLV_GET_STRUCT_TLVLEN(
				wmi_ndp_responder_req_fixed_param));
	cmd->vdev_id = req_params->vdev_id;
	cmd->transaction_id = req_params->transaction_id;
	cmd->ndp_instance_id = req_params->ndp_instance_id;
	cmd->rsp_code = req_params->ndp_rsp;

	cmd->ndp_cfg_len = req_params->ndp_config.ndp_cfg_len;
	cmd->ndp_app_info_len = req_params->ndp_info.ndp_app_info_len;
	cmd->nan_pmk_len = req_params->pmk.pmk_len;
	cmd->nan_csid = req_params->ncs_sk_type;

	tlv_ptr = (uint8_t *)&cmd[1];

	WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_cfg_len);
	vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
		req_params->ndp_config.ndp_cfg, cmd->ndp_cfg_len);
	tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_cfg_len;

	WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_app_info_len);
	vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
		     req_params->ndp_info.ndp_app_info,
		     req_params->ndp_info.ndp_app_info_len);
	tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_app_info_len;

	WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, pmk_len);
	vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE], req_params->pmk.pmk,
		     cmd->nan_pmk_len);
	tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + pmk_len;

	WMA_LOGE(FL("vdev_id = %d, transaction_id: %d, csid: %d"),
		cmd->vdev_id, cmd->transaction_id, cmd->nan_csid);

	WMA_LOGD(FL("ndp_config len: %d"),
		req_params->ndp_config.ndp_cfg_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			req_params->ndp_config.ndp_cfg,
			req_params->ndp_config.ndp_cfg_len);

	WMA_LOGD(FL("ndp_app_info len: %d"),
		req_params->ndp_info.ndp_app_info_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			req_params->ndp_info.ndp_app_info,
			req_params->ndp_info.ndp_app_info_len);

	WMA_LOGE(FL("pmk len: %d"), cmd->nan_pmk_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			   req_params->pmk.pmk, cmd->nan_pmk_len);

	WMA_LOGE(FL("sending WMI_NDP_RESPONDER_REQ_CMDID(0x%X)"),
		WMI_NDP_RESPONDER_REQ_CMDID);
	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_NDP_RESPONDER_REQ_CMDID);
	if (ret < 0) {
		WMA_LOGE(FL("WMI_NDP_RESPONDER_REQ_CMDID failed, ret: %d"),
			ret);
		wmi_buf_free(buf);
		goto send_ndp_responder_fail;
	}
	return VOS_STATUS_SUCCESS;
send_ndp_responder_fail:
	vos_mem_zero(&rsp, sizeof(rsp));
	rsp.vdev_id = req_params->vdev_id;
	rsp.transaction_id = req_params->transaction_id;
	rsp.status = NDP_RSP_STATUS_ERROR;
	rsp.reason = NDP_DATA_RESPONDER_REQ_FAILED;
	wma_send_ndp_responder_rsp(&rsp);
	return VOS_STATUS_E_FAILURE;
}

/**
 * wma_handle_ndp_end_req() - NDP end request handler
 * @wma_handle: wma handle
 * @ptr: request parameters
 *
 * Return: VOS_STATUS_SUCCESS on success; error number otherwise
 */
VOS_STATUS wma_handle_ndp_end_req(tp_wma_handle wma_handle, void *ptr)
{
	int ret;
	uint16_t len;
	uint32_t ndp_end_req_len, i;
	wmi_ndp_end_req *ndp_end_req_lst;
	wmi_buf_t buf;
	vos_msg_t pe_msg = {0};
	wmi_ndp_end_req_fixed_param *cmd;
	struct ndp_end_rsp_event *end_rsp = NULL;
	struct ndp_end_req *req = ptr;

	if (NULL == req) {
		WMA_LOGE(FL("Invalid ndp_end_req"));
		goto send_ndp_end_fail;
	}

	/* len of tlv following fixed param  */
	ndp_end_req_len = sizeof(wmi_ndp_end_req) * req->num_ndp_instances;
	/* above comes out to 4 byte alligned already, no need of padding */
	len = sizeof(*cmd) + ndp_end_req_len + WMI_TLV_HDR_SIZE;
	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("Malloc failed"));
		return VOS_STATUS_E_NOMEM;
	}
	cmd = (wmi_ndp_end_req_fixed_param *) wmi_buf_data(buf);

	WMITLV_SET_HDR(&cmd->tlv_header,
		       WMITLV_TAG_STRUC_wmi_ndp_end_req_fixed_param,
		       WMITLV_GET_STRUCT_TLVLEN(wmi_ndp_end_req_fixed_param));

	cmd->transaction_id = req->transaction_id;

	/* set tlv pointer to end of fixed param */
	WMITLV_SET_HDR((uint8_t *)&cmd[1], WMITLV_TAG_ARRAY_STRUC,
			ndp_end_req_len);

	ndp_end_req_lst = (wmi_ndp_end_req *)((uint8_t *)&cmd[1] +
						WMI_TLV_HDR_SIZE);
	for (i = 0; i < req->num_ndp_instances; i++) {
		WMITLV_SET_HDR(&ndp_end_req_lst[i],
				WMITLV_TAG_ARRAY_FIXED_STRUC,
				(sizeof(*ndp_end_req_lst) - WMI_TLV_HDR_SIZE));

		ndp_end_req_lst[i].ndp_instance_id = req->ndp_ids[i];
	}

	WMA_LOGD(FL("Sending WMI_NDP_END_REQ_CMDID to FW"));
	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_NDP_END_REQ_CMDID);
	if (ret < 0) {
		WMA_LOGE(FL("WMI_NDP_END_REQ_CMDID failed, ret: %d"), ret);
		wmi_buf_free(buf);
		goto send_ndp_end_fail;
	}
	return VOS_STATUS_SUCCESS;

send_ndp_end_fail:
	pe_msg.type = SIR_HAL_NDP_END_RSP;
	if (req) {
		end_rsp = vos_mem_malloc(sizeof(*end_rsp));
		if (NULL == end_rsp) {
			WMA_LOGE(FL("Malloc failed"));
			pe_msg.bodyval = true;
		} else {
			vos_mem_zero(end_rsp, sizeof(*end_rsp));
			end_rsp->status = NDP_RSP_STATUS_ERROR;
			end_rsp->reason = NDP_END_FAILED;
			end_rsp->transaction_id = req->transaction_id;
			pe_msg.bodyptr = end_rsp;
		}
	} else {
		pe_msg.bodyval = true;
	}

	if (VOS_STATUS_SUCCESS !=
		vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg)) {
		WMA_LOGE("NDP_END_RSP to PE failed");
		vos_mem_free(end_rsp);
	}
	return VOS_STATUS_E_FAILURE;
}

/**
 * wma_handle_ndp_sched_update_req() - NDP schedule update request handler
 * @wma_handle: wma handle
 * @req_params: request parameters
 *
 * Return: VOS_STATUS_SUCCESS on success; error number otherwise
 */
VOS_STATUS wma_handle_ndp_sched_update_req(tp_wma_handle wma_handle,
					struct ndp_end_req *req_params)
{
	return VOS_STATUS_SUCCESS;
}

/**
 * wma_ndp_indication_event_handler() - NDP indication event handler
 * @handle: wma handle
 * @event_info: event handler data
 * @len: length of event_info
 *
 * Handler for WMI_NDP_INDICATION_EVENTID
 * Return: 0 on success, negative errno on failure
 */
static int wma_ndp_indication_event_handler(void *handle, uint8_t  *event_info,
					    uint32_t len)
{
	WMI_NDP_INDICATION_EVENTID_param_tlvs *event;
	wmi_ndp_indication_event_fixed_param *fixed_params;
	vos_msg_t pe_msg = {0};
	struct ndp_indication_event *ind_event;
	VOS_STATUS status;
	size_t total_array_len = 0;

	event = (WMI_NDP_INDICATION_EVENTID_param_tlvs *)event_info;
	fixed_params =
		(wmi_ndp_indication_event_fixed_param *)event->fixed_param;

	if (fixed_params->ndp_cfg_len > event->num_ndp_cfg) {
		WMA_LOGE("FW message ndp cfg length %d larger than TLV hdr %d",
			 fixed_params->ndp_cfg_len, event->num_ndp_cfg);
		return -EINVAL;
	}

	if (fixed_params->ndp_app_info_len > event->num_ndp_app_info) {
		WMA_LOGE("FW message ndp app info length %d more than TLV hdr %d",
			 fixed_params->ndp_app_info_len, event->num_ndp_app_info);
		return -EINVAL;
	}

	if (fixed_params->ndp_cfg_len >
		(WMA_SVC_MSG_MAX_SIZE - sizeof(*fixed_params))) {
		WMA_LOGE("%s: excess wmi buffer: ndp_cfg_len %d",
			 __func__, fixed_params->ndp_cfg_len);
		return -EINVAL;
	} else {
		total_array_len = fixed_params->ndp_cfg_len +
					sizeof(*fixed_params);
	}

	if (fixed_params->ndp_app_info_len >
		(WMA_SVC_MSG_MAX_SIZE - total_array_len)) {
		WMA_LOGE("%s: excess wmi buffer: ndp_cfg_len %d",
			 __func__, fixed_params->ndp_app_info_len);
		return -EINVAL;
	} else {
		total_array_len += fixed_params->ndp_app_info_len;
	}

	if (fixed_params->nan_scid_len >
		(WMA_SVC_MSG_MAX_SIZE - total_array_len)) {
		WMA_LOGE("%s: excess wmi buffer: ndp_cfg_len %d",
			 __func__, fixed_params->nan_scid_len);
		return -EINVAL;
	}

	ind_event = vos_mem_malloc(sizeof(*ind_event));
	if (!ind_event) {
		WMA_LOGP(FL("Failed to allocate memory"));
		return VOS_STATUS_E_NOMEM;
	}
	vos_mem_zero(ind_event, sizeof(*ind_event));
	ind_event->vdev_id = fixed_params->vdev_id;
	ind_event->service_instance_id = fixed_params->service_instance_id;
	ind_event->ndp_instance_id = fixed_params->ndp_instance_id;
	ind_event->role = fixed_params->self_ndp_role;
	ind_event->policy = fixed_params->accept_policy;

	WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_params->peer_ndi_mac_addr,
				ind_event->peer_mac_addr.bytes);
	WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_params->peer_discovery_mac_addr,
				ind_event->peer_discovery_mac_addr.bytes);

	WMA_LOGD(FL("WMI_NDP_INDICATION_EVENTID(0x%X) received. vdev %d, \n"
		"service_instance %d, ndp_instance %d, role %d, policy %d, \n"
		"csid: %d, scid_len: %d, peer_mac_addr: %pM, peer_disc_mac_addr: %pM"),
		 WMI_NDP_INDICATION_EVENTID, fixed_params->vdev_id,
		 fixed_params->service_instance_id,
		 fixed_params->ndp_instance_id, fixed_params->self_ndp_role,
		 fixed_params->accept_policy,
		 fixed_params->nan_csid, fixed_params->nan_scid_len,
		 ind_event->peer_mac_addr.bytes,
		 ind_event->peer_discovery_mac_addr.bytes);

	WMA_LOGD(FL("ndp_cfg - %d bytes"), fixed_params->ndp_cfg_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			   &event->ndp_cfg, fixed_params->ndp_cfg_len);

	WMA_LOGD(FL("ndp_app_info - %d bytes"), fixed_params->ndp_app_info_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			&event->ndp_app_info, fixed_params->ndp_app_info_len);
	ind_event->ndp_config.ndp_cfg_len = fixed_params->ndp_cfg_len;
	ind_event->ndp_info.ndp_app_info_len = fixed_params->ndp_app_info_len;
	ind_event->ncs_sk_type = fixed_params->nan_csid;
	ind_event->scid.scid_len = fixed_params->nan_scid_len;

	if (ind_event->ndp_config.ndp_cfg_len) {
		ind_event->ndp_config.ndp_cfg =
			vos_mem_malloc(fixed_params->ndp_cfg_len);
		if (NULL == ind_event->ndp_config.ndp_cfg) {
			WMA_LOGE(FL("malloc failed"));
			vos_mem_free(ind_event);
			return VOS_STATUS_E_NOMEM;
		}
		vos_mem_copy(ind_event->ndp_config.ndp_cfg, event->ndp_cfg,
			     ind_event->ndp_config.ndp_cfg_len);
	}

	if (ind_event->ndp_info.ndp_app_info_len) {
		ind_event->ndp_info.ndp_app_info =
			vos_mem_malloc(ind_event->ndp_info.ndp_app_info_len);
		if (NULL == ind_event->ndp_info.ndp_app_info) {
			WMA_LOGE(FL("malloc failed"));
			vos_mem_free(ind_event->ndp_config.ndp_cfg);
			vos_mem_free(ind_event);
			return VOS_STATUS_E_NOMEM;
		}
		vos_mem_copy(ind_event->ndp_info.ndp_app_info,
			     event->ndp_app_info,
			     ind_event->ndp_info.ndp_app_info_len);
	}

	if (ind_event->scid.scid_len) {
		ind_event->scid.scid =
			vos_mem_malloc(ind_event->scid.scid_len);
		if (NULL == ind_event->scid.scid) {
			WMA_LOGE(FL("malloc failed"));
			vos_mem_free(ind_event->ndp_config.ndp_cfg);
			vos_mem_free(ind_event->ndp_info.ndp_app_info);
			vos_mem_free(ind_event);
			return VOS_STATUS_E_NOMEM;
		}
		vos_mem_copy(ind_event->scid.scid,
			     event->ndp_scid, ind_event->scid.scid_len);
		WMA_LOGD(FL("scid hex dump:"));
		VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
			ind_event->scid.scid, ind_event->scid.scid_len);
	}

	pe_msg.type = SIR_HAL_NDP_INDICATION;
	pe_msg.bodyptr = ind_event;
	pe_msg.bodyval = 0;

	WMA_LOGE(FL("Sending SIR_HAL_NDP_INDICATION msg to PE"));
	status = vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		WMA_LOGE(FL("fail to post SIR_HAL_NDP_INDICATION msg to PE"));
		vos_mem_free(ind_event->ndp_config.ndp_cfg);
		vos_mem_free(ind_event->ndp_info.ndp_app_info);
		vos_mem_free(ind_event->scid.scid);
		vos_mem_free(ind_event);
	}

	return status;
}

/**
 * wma_ndp_responder_rsp_event_handler() - NDP responder response event handler
 * @handle: wma handle
 * @event_info: event handler data
 * @len: length of event_info
 *
 * Handler for WMI_NDP_RESPONDER_RSP_EVENTID
 * Return: 0 on success, negative errno on failure
 */
static int wma_ndp_responder_rsp_event_handler(void *handle,
	uint8_t *event_info, uint32_t len)
{
	WMI_NDP_RESPONDER_RSP_EVENTID_param_tlvs *event;
	wmi_ndp_responder_rsp_event_fixed_param  *fixed_params;
	struct ndp_responder_rsp_event rsp;

	event = (WMI_NDP_RESPONDER_RSP_EVENTID_param_tlvs *)event_info;
	fixed_params = event->fixed_param;

	rsp.vdev_id = fixed_params->vdev_id;
	rsp.transaction_id = fixed_params->transaction_id;
	rsp.reason = fixed_params->reason_code;
	rsp.status = fixed_params->rsp_status;
	rsp.create_peer = fixed_params->create_peer;
	WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_params->peer_ndi_mac_addr,
				rsp.peer_mac_addr.bytes);

	WMA_LOGE(FL("WMI_NDP_RESPONDER_RSP_EVENTID(0x%X) received. vdev_id: %d, peer_mac_addr: %pM, transaction_id: %d, status_code %d, reason_code: %d, create_peer: %d"),
			WMI_NDP_RESPONDER_RSP_EVENTID, rsp.vdev_id,
			rsp.peer_mac_addr.bytes, rsp.transaction_id,
			rsp.status, rsp.reason, rsp.create_peer);

	return wma_send_ndp_responder_rsp(&rsp);
}

/**
 * wma_ndp_confirm_event_handler() - NDP confirm event handler
 * @handle: wma handle
 * @event_info: event handler data
 * @len: length of event_info
 *
 * Handler for WMI_NDP_CONFIRM_EVENTID
 * Return: 0 on success, negative errno on failure
 */
static int wma_ndp_confirm_event_handler(void *handle, uint8_t *event_info,
					 uint32_t len)
{
	struct ndp_confirm_event *ndp_confirm;
	vos_msg_t msg = {0};
	WMI_NDP_CONFIRM_EVENTID_param_tlvs *event;
	wmi_ndp_confirm_event_fixed_param *fixed_params;
	VOS_STATUS status;

	event = (WMI_NDP_CONFIRM_EVENTID_param_tlvs *) event_info;
	fixed_params = (wmi_ndp_confirm_event_fixed_param *)event->fixed_param;
	WMA_LOGE(FL("WMI_NDP_CONFIRM_EVENTID(0x%X) recieved. vdev %d, ndp_instance %d, rsp_code %d, reason_code: %d, num_active_ndps_on_peer: %d"),
		 WMI_NDP_CONFIRM_EVENTID, fixed_params->vdev_id,
		 fixed_params->ndp_instance_id, fixed_params->rsp_code,
		 fixed_params->reason_code,
		 fixed_params->num_active_ndps_on_peer);

	if (fixed_params->ndp_cfg_len > event->num_ndp_cfg) {
		WMA_LOGE("FW message ndp cfg length %d larger than TLV hdr %d",
			 fixed_params->ndp_cfg_len, event->num_ndp_cfg);
		return -EINVAL;
	}
	WMA_LOGE(FL("ndp_cfg - %d bytes"), fixed_params->ndp_cfg_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
		&event->ndp_cfg, fixed_params->ndp_cfg_len);

	if (fixed_params->ndp_app_info_len > event->num_ndp_app_info) {
		WMA_LOGE("FW message ndp app info length %d more than TLV hdr %d",
			 fixed_params->ndp_app_info_len, event->num_ndp_app_info);
		return -EINVAL;
	}
	WMA_LOGE(FL("ndp_app_info - %d bytes"), fixed_params->ndp_app_info_len);
	VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
		&event->ndp_app_info, fixed_params->ndp_app_info_len);

	ndp_confirm = vos_mem_malloc(sizeof(*ndp_confirm));
	if (!ndp_confirm) {
		WMA_LOGP(FL("Failed to allocate memory"));
		return VOS_STATUS_E_NOMEM;
	}
	vos_mem_zero(ndp_confirm, sizeof(*ndp_confirm));

	ndp_confirm->vdev_id = fixed_params->vdev_id;
	ndp_confirm->ndp_instance_id = fixed_params->ndp_instance_id;
	ndp_confirm->rsp_code = fixed_params->rsp_code;
	ndp_confirm->reason_code = fixed_params->reason_code;
	ndp_confirm->num_active_ndps_on_peer =
				fixed_params->num_active_ndps_on_peer;

	WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_params->peer_ndi_mac_addr,
				   ndp_confirm->peer_ndi_mac_addr.bytes);

	ndp_confirm->ndp_info.ndp_app_info_len = fixed_params->ndp_app_info_len;
	if (ndp_confirm->ndp_info.ndp_app_info_len) {
		ndp_confirm->ndp_info.ndp_app_info =
				vos_mem_malloc(fixed_params->ndp_app_info_len);
		if (NULL == ndp_confirm->ndp_info.ndp_app_info) {
			WMA_LOGE(FL("malloc failed"));
			vos_mem_free(ndp_confirm);
			return VOS_STATUS_E_NOMEM;
		}
		vos_mem_copy(&ndp_confirm->ndp_info.ndp_app_info,
			     event->ndp_app_info,
			     ndp_confirm->ndp_info.ndp_app_info_len);
	}
	msg.type = SIR_HAL_NDP_CONFIRM;
	msg.bodyptr = ndp_confirm;
	msg.bodyval = 0;
	WMA_LOGE(FL("Sending SIR_HAL_NDP_CONFIRM msg to PE"));
	status = vos_mq_post_message(VOS_MODULE_ID_PE, &msg);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		WMA_LOGE(FL("fail to post SIR_HAL_NDP_CONFIRM msg to PE"));
		vos_mem_free(ndp_confirm->ndp_info.ndp_app_info);
		vos_mem_free(ndp_confirm);
	}

	return VOS_STATUS_SUCCESS;
}

/**
 * wma_ndp_end_response_event_handler() - NDP end response event handler
 * @handle: wma handle
 * @event_info: event handler data
 * @len: length of event_info
 *
 * Handler for WMI_NDP_END_RSP_EVENTID
 * Return: 0 on success, negative errno on failure
 */
static int wma_ndp_end_response_event_handler(void *handle,
	uint8_t  *event_info, uint32_t len)
{
	int ret = 0;
	VOS_STATUS status;
	vos_msg_t pe_msg = {0};
	struct ndp_end_rsp_event *end_rsp;
	WMI_NDP_END_RSP_EVENTID_param_tlvs *event;
	wmi_ndp_end_rsp_event_fixed_param *fixed_params = NULL;

	event = (WMI_NDP_END_RSP_EVENTID_param_tlvs *) event_info;
	fixed_params = (wmi_ndp_end_rsp_event_fixed_param *)event->fixed_param;
	WMA_LOGD(FL("WMI_NDP_END_RSP_EVENTID(0x%X) recieved. transaction_id: %d, rsp_status: %d, reason_code: %d"),
		 WMI_NDP_END_RSP_EVENTID, fixed_params->transaction_id,
		 fixed_params->rsp_status, fixed_params->reason_code);

	end_rsp = vos_mem_malloc(sizeof(*end_rsp));
	if (NULL == end_rsp) {
		WMA_LOGE("malloc failed");
		pe_msg.bodyval = true;
		ret = -ENOMEM;
		goto send_ndp_end_rsp;
	}
	pe_msg.bodyptr = end_rsp;
	vos_mem_zero(end_rsp, sizeof(*end_rsp));

	end_rsp->transaction_id = fixed_params->transaction_id;
	end_rsp->reason = fixed_params->reason_code;
	end_rsp->status = fixed_params->rsp_status;

send_ndp_end_rsp:
	pe_msg.type = SIR_HAL_NDP_END_RSP;
	WMA_LOGD(FL("Sending SIR_HAL_NDP_END_RSP msg to PE"));
	status = vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		WMA_LOGE("SIR_HAL_NDP_END_RSP to PE failed");
		vos_mem_free(end_rsp);
		ret = -EINVAL;
	}

	return ret;
}

/**
 * wma_ndp_end_indication_event_handler() - NDP end indication event handler
 * @handle: wma handle
 * @event_info: event handler data
 * @len: length of event_info
 *
 * Handler for WMI_NDP_END_INDICATION_EVENTID
 * Return: 0 on success, negative errno on failure
 */
static int wma_ndp_end_indication_event_handler(void *handle,
	uint8_t  *event_info, uint32_t len)
{
	WMI_NDP_END_INDICATION_EVENTID_param_tlvs *event;
	wmi_ndp_end_indication *ind;
	vos_msg_t pe_msg;
	struct ndp_end_indication_event *ndp_event_buf;
	VOS_STATUS vos_status;
	int i;
	v_MACADDR_t peer_addr;
	int buf_size;

	event = (WMI_NDP_END_INDICATION_EVENTID_param_tlvs *) event_info;

	if (event->num_ndp_end_indication_list == 0) {
		WMA_LOGE(
			FL("Error: Event ignored, 0 ndp instances"));
		return -EINVAL;
	}

	WMA_LOGD(FL("number of ndp instances = %d"),
		event->num_ndp_end_indication_list);
	if (event->num_ndp_end_indication_list > ((WMA_SVC_MSG_MAX_SIZE -
		sizeof(*ndp_event_buf)) / sizeof(ndp_event_buf->ndp_map[0]))) {
			WMA_LOGE("%s: excess data received from fw num_ndp_end_indication_list %d",
				__func__, event->num_ndp_end_indication_list);
			return -EINVAL;
	}
	buf_size = sizeof(*ndp_event_buf) + event->num_ndp_end_indication_list *
			sizeof(ndp_event_buf->ndp_map[0]);
	ndp_event_buf = vos_mem_malloc(buf_size);
	if (!ndp_event_buf) {
		WMA_LOGP(FL("Failed to allocate memory"));
		return -ENOMEM;
	}
	vos_mem_zero(ndp_event_buf, buf_size);
	ndp_event_buf->num_ndp_ids = event->num_ndp_end_indication_list;

	ind = event->ndp_end_indication_list;
	for (i = 0; i < ndp_event_buf->num_ndp_ids; i++) {
		WMI_MAC_ADDR_TO_CHAR_ARRAY(
			&ind[i].peer_ndi_mac_addr,
			peer_addr.bytes);
		WMA_LOGD(
			FL("ind[%d]: type %d, reason_code %d, instance_id %d num_active %d MAC: " MAC_ADDRESS_STR),
			i,
			ind[i].type,
			ind[i].reason_code,
			ind[i].ndp_instance_id,
			ind[i].num_active_ndps_on_peer,
			MAC_ADDR_ARRAY(peer_addr.bytes));

		/* Add each instance entry to the list */
		ndp_event_buf->ndp_map[i].ndp_instance_id =
			ind[i].ndp_instance_id;
		ndp_event_buf->ndp_map[i].vdev_id = ind[i].vdev_id;
		WMI_MAC_ADDR_TO_CHAR_ARRAY(&ind[i].peer_ndi_mac_addr,
			ndp_event_buf->ndp_map[i].peer_ndi_mac_addr.bytes);
		ndp_event_buf->ndp_map[i].num_active_ndp_sessions =
			ind[i].num_active_ndps_on_peer;
		ndp_event_buf->ndp_map[i].type = ind[i].type;
		ndp_event_buf->ndp_map[i].reason_code =
			ind[i].reason_code;
	}

	pe_msg.type = SIR_HAL_NDP_END_IND;
	pe_msg.bodyptr = ndp_event_buf;
	pe_msg.bodyval = 0;
	vos_status = vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg);
	if (VOS_IS_STATUS_SUCCESS(vos_status)) {
		return 0;
	}

	WMA_LOGE(FL("failed to post msg to PE"));
	vos_mem_free(ndp_event_buf);
	return -EINVAL;
}

/**
 * wma_ndp_initiator_rsp_event_handler() -NDP initiator rsp event handler
 * @handle: wma handle
 * @event_info: event handler data
 * @len: length of event_info
 *
 * Handler for WMI_NDP_INITIATOR_RSP_EVENTID
 * Return: 0 on success, negative errno on failure
 */
static int wma_ndp_initiator_rsp_event_handler(void *handle,
					uint8_t *event_info, uint32_t len)
{
	VOS_STATUS status;
	vos_msg_t pe_msg = {0};
	WMI_NDP_INITIATOR_RSP_EVENTID_param_tlvs *event;
	wmi_ndp_initiator_rsp_event_fixed_param  *fixed_params;
	struct ndp_initiator_rsp *rsp;

	rsp = vos_mem_malloc(sizeof(*rsp));
	if (NULL == rsp) {
		WMA_LOGE(FL("Invalid rsp_ind"));
		return VOS_STATUS_E_INVAL;
	}

	event = (WMI_NDP_INITIATOR_RSP_EVENTID_param_tlvs *)event_info;
	fixed_params = event->fixed_param;

	rsp->vdev_id = fixed_params->vdev_id;
	rsp->transaction_id = fixed_params->transaction_id;
	rsp->ndp_instance_id = fixed_params->ndp_instance_id;
	rsp->status = fixed_params->rsp_status;
	rsp->reason = fixed_params->reason_code;

	pe_msg.type = SIR_HAL_NDP_INITIATOR_RSP;
	pe_msg.bodyptr = rsp;
	status = vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg);
	if (!VOS_IS_STATUS_SUCCESS(status)) {
		WMA_LOGE("SIR_HAL_NDP_INITIATOR_RSP to PE failed");
		vos_mem_free(rsp);
		return status;
	}

	return status;
}

/**
 * wma_ndp_register_all_event_handlers() - Register all NDP event handlers
 * @wma_handle: WMA context
 *
 * Register the event handlers for NAN data path events from firmware.
 *
 * Return: None
 */
void wma_ndp_register_all_event_handlers(tp_wma_handle wma_handle)
{
	WMA_LOGD(FL("Register WMI_NDP_INITIATOR_RSP_EVENTID"));
	wmi_unified_register_event_handler(wma_handle->wmi_handle,
		WMI_NDP_INITIATOR_RSP_EVENTID,
		wma_ndp_initiator_rsp_event_handler);

	WMA_LOGD(FL("Register WMI_NDP_RESPONDER_RSP_EVENTID"));
	wmi_unified_register_event_handler(wma_handle->wmi_handle,
		WMI_NDP_RESPONDER_RSP_EVENTID,
		wma_ndp_responder_rsp_event_handler);

	WMA_LOGD(FL("Register WMI_NDP_END_RSP_EVENTID"));
	wmi_unified_register_event_handler(wma_handle->wmi_handle,
		WMI_NDP_END_RSP_EVENTID,
		wma_ndp_end_response_event_handler);

	WMA_LOGD(FL("Register WMI_NDP_INDICATION_EVENTID"));
	wmi_unified_register_event_handler(wma_handle->wmi_handle,
		WMI_NDP_INDICATION_EVENTID,
		wma_ndp_indication_event_handler);

	WMA_LOGD(FL("Register WMI_NDP_CONFIRM_EVENTID"));
	wmi_unified_register_event_handler(wma_handle->wmi_handle,
		WMI_NDP_CONFIRM_EVENTID,
		wma_ndp_confirm_event_handler);

	WMA_LOGD(FL("Register WMI_NDP_END_INDICATION_EVENTID"));
	wmi_unified_register_event_handler(wma_handle->wmi_handle,
		WMI_NDP_END_INDICATION_EVENTID,
		wma_ndp_end_indication_event_handler);
}

/**
 * wma_ndp_unregister_all_event_handlers() - Unregister all NDP event handlers
 * @wma_handle: WMA context
 *
 * Register the event handlers for NAN data path events from firmware.
 *
 * Return: None
 */
void wma_ndp_unregister_all_event_handlers(tp_wma_handle wma_handle)
{
	WMA_LOGD(FL("Unregister WMI_NDP_INITIATOR_RSP_EVENTID"));
	wmi_unified_unregister_event_handler(wma_handle->wmi_handle,
		WMI_NDP_INITIATOR_RSP_EVENTID);

	WMA_LOGD(FL("Unregister WMI_NDP_RESPONDER_RSP_EVENTID"));
	wmi_unified_unregister_event_handler(wma_handle->wmi_handle,
		WMI_NDP_RESPONDER_RSP_EVENTID);

	WMA_LOGD(FL("Unregister WMI_NDP_END_RSP_EVENTID"));
	wmi_unified_unregister_event_handler(wma_handle->wmi_handle,
		WMI_NDP_END_RSP_EVENTID);

	WMA_LOGD(FL("Unregister WMI_NDP_INDICATION_EVENTID"));
	wmi_unified_unregister_event_handler(wma_handle->wmi_handle,
		WMI_NDP_INDICATION_EVENTID);

	WMA_LOGD(FL("Unregister WMI_NDP_CONFIRM_EVENTID"));
	wmi_unified_unregister_event_handler(wma_handle->wmi_handle,
		WMI_NDP_CONFIRM_EVENTID);

	WMA_LOGD(FL("Unregister WMI_NDP_END_INDICATION_EVENTID"));
	wmi_unified_unregister_event_handler(wma_handle->wmi_handle,
		WMI_NDP_END_INDICATION_EVENTID);
}

/**
 * wma_ndp_add_wow_wakeup_event() - Add Wake on Wireless event for NDP
 * @wma_handle: WMA context
 * @enable: dis/enable flag to enable the bit for WOW_NAN_DATA_EVENT
 *
 * Enables the firmware to wake up the host on NAN data path event.
 * All NDP events such as NDP_INDICATION, NDP_CONFIRM, etc. use the
 * same event. They can be distinguished using their TLV tags.
 *
 * Return: none
 */
void wma_ndp_add_wow_wakeup_event(tp_wma_handle wma_handle,
						bool enable)
{
	wma_add_wow_wakeup_event(wma_handle, WOW_NAN_DATA_EVENT, enable);
}

/**
 * wma_ndp_get_eventid_from_tlvtag() - map tlv tag to event id
 * @tag: WMI TLV tag
 *
 * map the tag to known NDP event fixed_param tags and return the
 * corresponding NDP event id.
 *
 * Return: 0 if TLV tag is invalid
 *           else return corresponding WMI event id
 */
static int wma_ndp_get_eventid_from_tlvtag(uint32_t tag)
{
	uint32_t event_id;

	switch (tag) {
	case WMITLV_TAG_STRUC_wmi_ndp_initiator_rsp_event_fixed_param:
		event_id = WMI_NDP_INITIATOR_RSP_EVENTID;
		break;

	case WMITLV_TAG_STRUC_wmi_ndp_responder_rsp_event_fixed_param:
		event_id = WMI_NDP_RESPONDER_RSP_EVENTID;
		break;

	case WMITLV_TAG_STRUC_wmi_ndp_end_rsp_event_fixed_param:
		event_id = WMI_NDP_END_RSP_EVENTID;
		break;

	case WMITLV_TAG_STRUC_wmi_ndp_indication_event_fixed_param:
		event_id = WMI_NDP_INDICATION_EVENTID;
		break;

	case WMITLV_TAG_STRUC_wmi_ndp_confirm_event_fixed_param:
		event_id = WMI_NDP_CONFIRM_EVENTID;
		break;

	case WMITLV_TAG_STRUC_wmi_ndp_end_indication_event_fixed_param:
		event_id = WMI_NDP_END_INDICATION_EVENTID;
		break;

	default:
		event_id = 0;
		WMA_LOGE(FL("Unknown tag: %d"), tag);
		break;
	}

	WMA_LOGI(FL("For tag %d WMI event 0x%x"), tag, event_id);
	return event_id;
}

/**
 * wma_ndp_wow_event_callback() - NAN data path wow event callback
 * @handle: WMA handle
 * @event: event buffer
 * @len: length of @event buffer
 *
 * The wow event WOW_REASON_NAN_DATA is followed by the payload of the event
 * which generated the wow event.
 * Payload is 4 bytes of length followed by event buffer. First 4 bytes
 * of event buffer is common tlv header, which is a combination
 * of tag (higher 2 bytes) and length (lower 2 bytes). The tag is used to
 * identify the event which triggered wow event.
 *
 * Return: none
 */
void wma_ndp_wow_event_callback(void *handle, void *event,
						  uint32_t len)
{
	uint32_t id;
	int tlv_ok_status = 0;
	void *wmi_cmd_struct_ptr = NULL;
	uint32_t tag = WMITLV_GET_TLVTAG(WMITLV_GET_HDR(event));

	/* Reverse map fixed params tag to EVENT_ID */
	id = wma_ndp_get_eventid_from_tlvtag(tag);
	if (!id) {
		WMA_LOGE(FL("Invalid  Tag: %d"), tag);
		return;
	}

	tlv_ok_status = wmitlv_check_and_pad_event_tlvs(handle, event, len,
							id,
							&wmi_cmd_struct_ptr);
	if (tlv_ok_status != 0) {
		WMA_LOGE(FL("Invalid Tag: %d could not check and pad tlvs"),
			 tag);
		return;
	}

	switch (id) {
	case WMI_NDP_INITIATOR_RSP_EVENTID:
		wma_ndp_initiator_rsp_event_handler(handle,
						wmi_cmd_struct_ptr, len);
		break;

	case WMI_NDP_RESPONDER_RSP_EVENTID:
		wma_ndp_responder_rsp_event_handler(handle,
						wmi_cmd_struct_ptr, len);
		break;

	case WMI_NDP_END_RSP_EVENTID:
		wma_ndp_end_response_event_handler(handle,
						wmi_cmd_struct_ptr, len);
		break;

	case WMI_NDP_INDICATION_EVENTID:
		wma_ndp_indication_event_handler(handle,
						wmi_cmd_struct_ptr, len);
		break;

	case WMI_NDP_CONFIRM_EVENTID:
		wma_ndp_confirm_event_handler(handle,
						wmi_cmd_struct_ptr, len);
		break;

	case WMI_NDP_END_INDICATION_EVENTID:
		wma_ndp_end_indication_event_handler(handle,
						wmi_cmd_struct_ptr, len);
		break;

	default:
		WMA_LOGE(FL("Unknown tag: %d"), tag);
		break;
	}
	wmitlv_free_allocated_event_tlvs(id, &wmi_cmd_struct_ptr);
}

/**
 * wma_add_bss_ndi_mode() - Process BSS creation request while adding NaN
 * Data interface
 * @wma: wma handle
 * @add_bss: Parameters for ADD_BSS command
 *
 * Sends VDEV_START command to firmware
 * Return: None
 */
void wma_add_bss_ndi_mode(tp_wma_handle wma, tpAddBssParams add_bss)
{
	ol_txrx_pdev_handle pdev;
	struct wma_vdev_start_req req;
	ol_txrx_peer_handle peer = NULL;
	struct wma_target_req *msg;
	uint8_t vdev_id, peer_id;
	VOS_STATUS status;
	uint8_t nss_2g, nss_5g;

	WMA_LOGE("%s: enter", __func__);
	if (NULL == wma_find_vdev_by_addr(wma, add_bss->bssId, &vdev_id)) {
		WMA_LOGE("%s: Failed to find vdev", __func__);
		goto send_fail_resp;
	}
	pdev = vos_get_context(VOS_MODULE_ID_TXRX, wma->vos_context);

	if (NULL == pdev) {
		WMA_LOGE("%s: Failed to get pdev", __func__);
		goto send_fail_resp;
	}

	nss_2g = wma->interfaces[vdev_id].nss_2g;
	nss_5g = wma->interfaces[vdev_id].nss_5g;
	wma_set_bss_rate_flags(&wma->interfaces[vdev_id], add_bss);

	peer = ol_txrx_find_peer_by_addr(pdev, add_bss->selfMacAddr, &peer_id);
	if (!peer) {
		WMA_LOGE("%s Failed to find peer %pM", __func__,
			add_bss->selfMacAddr);
		goto send_fail_resp;
	}

	msg = wma_fill_vdev_req(wma, vdev_id, WDA_ADD_BSS_REQ,
			WMA_TARGET_REQ_TYPE_VDEV_START, add_bss,
			WMA_VDEV_START_REQUEST_TIMEOUT);
	if (!msg) {
		WMA_LOGE("%s Failed to allocate vdev request vdev_id %d",
			 __func__, vdev_id);
		goto send_fail_resp;
	}

	add_bss->staContext.staIdx = ol_txrx_local_peer_id(peer);

	/*
	 * beacon_intval, dtim_period, hidden_ssid, is_dfs, ssid
	 * will be ignored for NDI device.
	 */
	vos_mem_zero(&req, sizeof(req));
	req.vdev_id = vdev_id;
	req.chan = add_bss->currentOperChannel;
	req.chan_offset = add_bss->currentExtChannel;
	req.vht_capable = add_bss->vhtCapable;
	req.max_txpow = add_bss->maxTxPower;
	req.oper_mode = add_bss->operMode;

	status = wma_vdev_start(wma, &req, VOS_FALSE);
	if (status != VOS_STATUS_SUCCESS) {
		wma_remove_vdev_req(wma, vdev_id,
			WMA_TARGET_REQ_TYPE_VDEV_START);
		goto send_fail_resp;
	}
	WMA_LOGI("%s: vdev start request for NDI sent to target", __func__);

	/* Initialize protection mode to no protection */
	if (wmi_unified_vdev_set_param_send(wma->wmi_handle, vdev_id,
		WMI_VDEV_PARAM_PROTECTION_MODE,
		IEEE80211_PROT_NONE)) {
		WMA_LOGE("Failed to initialize protection mode");
	}

	return;
send_fail_resp:
	add_bss->status = VOS_STATUS_E_FAILURE;
	wma_send_msg(wma, WDA_ADD_BSS_RSP, (void *)add_bss, 0);
}

/**
 * wma_delete_all_nan_remote_peers() - Delete all nan peer
 * @wma:  wma handle
 * @vdev_id: vdev id
 *
 * Return: void
 */
void wma_delete_all_nan_remote_peers(tp_wma_handle wma, uint32_t vdev_id)
{
	ol_txrx_vdev_handle vdev;
	ol_txrx_peer_handle peer, temp;

	if (!wma || vdev_id >= wma->max_bssid)
		return;

	vdev = wma->interfaces[vdev_id].handle;
	if (!vdev)
		return;

	/* remove all remote peers of ndi*/
	adf_os_spin_lock_bh(&vdev->pdev->peer_ref_mutex);

	temp = NULL;
	TAILQ_FOREACH_REVERSE(peer, &vdev->peer_list,
		peer_list_t, peer_list_elem) {
		if (temp) {
			adf_os_spin_unlock_bh(&vdev->pdev->peer_ref_mutex);
			if (adf_os_atomic_read(
				&temp->delete_in_progress) == 0)
				wma_remove_peer(wma, temp->mac_addr.raw,
					vdev_id, temp, VOS_FALSE);
			adf_os_spin_lock_bh(&vdev->pdev->peer_ref_mutex);
		}
		/* self peer is deleted last */
		if (peer == TAILQ_FIRST(&vdev->peer_list)) {
			WMA_LOGE("%s: self peer removed", __func__);
			break;
		} else
			temp = peer;
	}
	adf_os_spin_unlock_bh(&vdev->pdev->peer_ref_mutex);

	/* remove ndi self peer last */
	peer = TAILQ_FIRST(&vdev->peer_list);
	wma_remove_peer(wma, peer->mac_addr.raw, vdev_id, peer,
			false);
}
/**
 * wma_add_sta_ndi_mode() - Process ADD_STA for NaN Data path
 * @wma: wma handle
 * @add_sta: Parameters of ADD_STA command
 *
 * Sends CREATE_PEER command to firmware
 * Return: void
 */
void wma_add_sta_ndi_mode(tp_wma_handle wma, tpAddStaParams add_sta)
{
	enum ol_txrx_peer_state state = ol_txrx_peer_state_conn;
	ol_txrx_pdev_handle pdev;
	ol_txrx_vdev_handle vdev;
	ol_txrx_peer_handle peer;
	u_int8_t peer_id;
	VOS_STATUS status;
	struct wma_txrx_node *iface;

	pdev = vos_get_context(VOS_MODULE_ID_TXRX, wma->vos_context);

	if (NULL == pdev) {
		WMA_LOGE(FL("Failed to find pdev"));
		add_sta->status = VOS_STATUS_E_FAILURE;
		goto send_rsp;
	}

	vdev = wma_find_vdev_by_id(wma, add_sta->smesessionId);
	if (!vdev) {
		WMA_LOGE(FL("Failed to find vdev"));
		add_sta->status = VOS_STATUS_E_FAILURE;
		goto send_rsp;
	}

	iface = &wma->interfaces[vdev->vdev_id];
	WMA_LOGD(FL("vdev: %d, peer_mac_addr: "MAC_ADDRESS_STR),
		add_sta->smesessionId, MAC_ADDR_ARRAY(add_sta->staMac));

	peer = ol_txrx_find_peer_by_addr_and_vdev(pdev, vdev, add_sta->staMac,
						  &peer_id);
	if (peer) {
		WMA_LOGE(FL("NDI peer already exists, peer_addr %pM"),
			 add_sta->staMac);
		add_sta->status = VOS_STATUS_E_EXISTS;
		goto send_rsp;
	}

	/*
	 * The code above only checks the peer existence on its own vdev.
	 * Need to check whether the peer exists on other vDevs because firmware
	 * can't create the peer if the peer with same MAC address already
	 * exists on the pDev. As this peer belongs to other vDevs, just return
	 * here.
	 */
	peer = ol_txrx_find_peer_by_addr(pdev, add_sta->staMac, &peer_id);
	if (peer) {
		WMA_LOGE(FL("vdev:%d, peer exists on other vdev with peer_addr %pM and peer_id %d"),
			 vdev->vdev_id, add_sta->staMac, peer_id);
		add_sta->status = VOS_STATUS_E_EXISTS;
		goto send_rsp;
	}

	status = wma_create_peer(wma, pdev, vdev, add_sta->staMac,
				 WMI_PEER_TYPE_NAN_DATA, add_sta->smesessionId,
				 VOS_FALSE);
	if (status != VOS_STATUS_SUCCESS) {
		WMA_LOGE(FL("Failed to create peer for %pM"), add_sta->staMac);
		add_sta->status = status;
		goto send_rsp;
	}

	peer = ol_txrx_find_peer_by_addr_and_vdev(pdev, vdev, add_sta->staMac,
						  &peer_id);
	if (!peer) {
		WMA_LOGE(FL("Failed to find peer handle using peer mac %pM"),
			 add_sta->staMac);
		add_sta->status = VOS_STATUS_E_FAILURE;
		wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId,
				peer, VOS_FALSE);
		goto send_rsp;
	}

	WMA_LOGD(FL("Moving peer %pM to state %d"), add_sta->staMac, state);
	ol_txrx_peer_state_update(pdev, add_sta->staMac, state);

	add_sta->staIdx = ol_txrx_local_peer_id(peer);
	add_sta->nss    = iface->nss;
	add_sta->status = VOS_STATUS_SUCCESS;
send_rsp:
	WMA_LOGD(FL("Sending add sta rsp to umac (mac:%pM, status:%d)"),
		 add_sta->staMac, add_sta->status);
	wma_send_msg(wma, WDA_ADD_STA_RSP, (void *)add_sta, 0);
}

/**
 * wma_delete_sta_req_ndi_mode() - Process DEL_STA request for NDI data peer
 * @wma: WMA context
 * @del_sta: DEL_STA parameters from LIM
 *
 * Removes wma/txrx peer entry for the NDI STA
 *
 * Return: None
 */
void wma_delete_sta_req_ndi_mode(tp_wma_handle wma,
					tpDeleteStaParams del_sta)
{
	ol_txrx_pdev_handle pdev;
	struct ol_txrx_peer_t *peer;

	pdev = vos_get_context(VOS_MODULE_ID_TXRX, wma->vos_context);

	if (!pdev) {
		WMA_LOGE(FL("Failed to get pdev"));
		del_sta->status = VOS_STATUS_E_FAILURE;
		goto send_del_rsp;
	}

	peer = ol_txrx_peer_find_by_local_id(pdev, del_sta->staIdx);
	if (!peer) {
		WMA_LOGE(FL("Failed to get peer handle using peer id %d"),
			 del_sta->staIdx);
		del_sta->status = VOS_STATUS_E_FAILURE;
		goto send_del_rsp;
	}

	wma_remove_peer(wma, peer->mac_addr.raw, del_sta->smesessionId, peer,
			false);
	del_sta->status = VOS_STATUS_SUCCESS;

send_del_rsp:
	if (del_sta->respReqd) {
		WMA_LOGD(FL("Sending del rsp to umac (status: %d)"),
				del_sta->status);
		wma_send_msg(wma, WDA_DELETE_STA_RSP, del_sta, 0);
	}
}


