/*
 * Copyright (c) 2015-2017 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.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**
 * DOC: wma_ocb.c
 *
 * WLAN Host Device Driver 802.11p OCB implementation
 */

#include "wma_ocb.h"
#include "wmi_unified_api.h"
#include "utilsApi.h"

#define UINT32_MAX (0xFFFFFFFFU)

/**
 * wma_ocb_resp() - send the OCB set config response via callback
 * @wma_handle: pointer to the WMA handle
 * @status: status of the set config command
 */
int wma_ocb_set_config_resp(tp_wma_handle wma_handle, uint8_t status)
{
	VOS_STATUS vos_status;
	struct sir_ocb_set_config_response *resp;
	vos_msg_t msg = {0};
	struct sir_ocb_config *req = wma_handle->ocb_config_req;
	ol_txrx_vdev_handle vdev = (req ?
		wma_handle->interfaces[req->session_id].handle : 0);

	if (status != VOS_STATUS_SUCCESS)
		goto out;

	/* If config succeeded, save the channel information in the vdev. */
	if (vdev && req) {
		if (vdev->ocb_channel_info)
			vos_mem_free(vdev->ocb_channel_info);
		vdev->ocb_channel_count = req->channel_count;
		if (req->channel_count) {
			int i;
			int buf_size = sizeof(*vdev->ocb_channel_info) *
				req->channel_count;
			vdev->ocb_channel_info = vos_mem_malloc(buf_size);
			if (!vdev->ocb_channel_info)
				return -ENOMEM;
			vos_mem_zero(vdev->ocb_channel_info, buf_size);
			for (i = 0; i < req->channel_count; i++) {
				vdev->ocb_channel_info[i].chan_freq =
					req->channels[i].chan_freq;
				vdev->ocb_channel_info[i].bandwidth =
					req->channels[i].bandwidth;
				if (req->channels[i].flags &
				    OCB_CHANNEL_FLAG_DISABLE_RX_STATS_HDR)
					vdev->ocb_channel_info[i].
						disable_rx_stats_hdr = 1;
				vos_mem_copy(
					vdev->ocb_channel_info[i].mac_address,
					req->channels[i].mac_address,
					sizeof(req->channels[i].mac_address));
			}
		} else {
			vdev->ocb_channel_info = 0;
		}

		vdev->ocb_config_flags = req->flags;

		/* Default TX parameter */
		if (!ol_txrx_set_ocb_def_tx_param(vdev,
		    req->def_tx_param, req->def_tx_param_size)) {
			/* Setting the default param failed */
			WMA_LOGE(FL("Invalid default TX parameters"));
			status = VOS_STATUS_E_INVAL;
		}
	}

out:
	/* Free the configuration that was saved in wma_ocb_set_config. */
	vos_mem_free(wma_handle->ocb_config_req);
	wma_handle->ocb_config_req = 0;

	resp = vos_mem_malloc(sizeof(*resp));
	if (!resp)
		return -ENOMEM;

	resp->status = status;

	msg.type = eWNI_SME_OCB_SET_CONFIG_RSP;
	msg.bodyptr = resp;

	vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &msg);
	if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
		WMA_LOGE(FL("Fail to post msg to SME"));
		vos_mem_free(resp);
		return -EINVAL;
	}

	return 0;
}

/**
 * wma_ocb_set_config_req() - send the OCB config request
 * @wma_handle: pointer to the WMA handle
 * @config_req: the configuration to be set.
 */
int wma_ocb_set_config_req(tp_wma_handle wma_handle,
			   struct sir_ocb_config *config_req)
{
	struct wma_target_req *msg;
	struct wma_vdev_start_req req;
	VOS_STATUS status = VOS_STATUS_SUCCESS;

	/* if vdev is not yet up, send vdev start request and wait for response.
	 * OCB set_config request should be sent on receiving
	 * vdev start response message
	 */
	if (!wma_handle->interfaces[config_req->session_id].vdev_up) {
		vos_mem_zero(&req, sizeof(req));
		/* Enqueue OCB Set Schedule request message */
		msg = wma_fill_vdev_req(wma_handle, config_req->session_id,
					WDA_OCB_SET_CONFIG_CMD,
					WMA_TARGET_REQ_TYPE_VDEV_START,
					(void *)config_req, 1000);
		if (!msg) {
			WMA_LOGE(FL("Failed to fill vdev req %d"), req.vdev_id);
			status = VOS_STATUS_E_NOMEM;
			return status;
		}
		req.chan = vos_freq_to_chan(config_req->channels[0].chan_freq);
		req.vdev_id = msg->vdev_id;
		if (vos_chan_to_band(req.chan) == VOS_BAND_2GHZ)
		    req.dot11_mode = WNI_CFG_DOT11_MODE_11G;
		else
		    req.dot11_mode = WNI_CFG_DOT11_MODE_11A;

		if (wma_handle->ocb_config_req)
			vos_mem_free(wma_handle->ocb_config_req);
		wma_handle->ocb_config_req = sir_copy_sir_ocb_config(config_req);

		status = wma_vdev_start(wma_handle, &req, VOS_FALSE);
		if (status != VOS_STATUS_SUCCESS) {
			wma_remove_vdev_req(wma_handle, req.vdev_id,
					    WMA_TARGET_REQ_TYPE_VDEV_START);
			WMA_LOGE(FL("vdev_start failed, status = %d"), status);
		}
		return 0;
	} else {
		return wma_ocb_set_config(wma_handle, config_req);
	}
}

int wma_ocb_start_resp_ind_cont(tp_wma_handle wma_handle)
{
	VOS_STATUS vos_status = 0;

	if (!wma_handle->ocb_config_req) {
		WMA_LOGE(FL("The request could not be found"));
		return VOS_STATUS_E_EMPTY;
	}

	vos_status = wma_ocb_set_config(wma_handle, wma_handle->ocb_config_req);
	return vos_status;
}

static WLAN_PHY_MODE wma_ocb_freq_to_mode(uint32_t freq)
{
	if (vos_chan_to_band(vos_freq_to_chan(freq)) == VOS_BAND_2GHZ)
		return MODE_11G;
	else
		return MODE_11A;
}

/**
 * wma_send_ocb_set_config() - send the OCB config to the FW
 * @wma_handle: pointer to the WMA handle
 * @config: the OCB configuration
 *
 * Return: 0 on success
 */
int wma_ocb_set_config(tp_wma_handle wma_handle, struct sir_ocb_config *config)
{
	int32_t ret;
	wmi_ocb_set_config_cmd_fixed_param *cmd;
	wmi_channel *chan;
	wmi_ocb_channel *ocb_chan;
	wmi_qos_parameter *qos_param;
	wmi_dcc_ndl_chan *ndl_chan;
	wmi_dcc_ndl_active_state_config *ndl_active_config;
	wmi_ocb_schedule_element *sched_elem;
	uint8_t *buf_ptr;
	wmi_buf_t buf;
	int32_t len;
	int32_t i, j, active_state_count;

	/*
	 * Validate the dcc_ndl_chan_list_len and count the number of active
	 * states. Validate dcc_ndl_active_state_list_len.
	 */
	active_state_count = 0;
	if (config->dcc_ndl_chan_list_len) {
		if (!config->dcc_ndl_chan_list ||
			config->dcc_ndl_chan_list_len !=
			config->channel_count * sizeof(wmi_dcc_ndl_chan)) {
			WMA_LOGE(FL("NDL channel is invalid. List len: %d"),
				 config->dcc_ndl_chan_list_len);
			return -EINVAL;
		}

		for (i = 0, ndl_chan = config->dcc_ndl_chan_list;
				i < config->channel_count; ++i, ++ndl_chan)
			active_state_count +=
				WMI_NDL_NUM_ACTIVE_STATE_GET(ndl_chan);

		if (active_state_count) {
			if (!config->dcc_ndl_active_state_list ||
				config->dcc_ndl_active_state_list_len !=
				active_state_count *
				sizeof(wmi_dcc_ndl_active_state_config)) {
				WMA_LOGE(FL("NDL active state is invalid."));
				return -EINVAL;
			}
		}
	}

	len = sizeof(*cmd) +
		WMI_TLV_HDR_SIZE + config->channel_count *
			sizeof(wmi_channel) +
		WMI_TLV_HDR_SIZE + config->channel_count *
			sizeof(wmi_ocb_channel) +
		WMI_TLV_HDR_SIZE + config->channel_count *
			sizeof(wmi_qos_parameter) * WLAN_MAX_AC +
		WMI_TLV_HDR_SIZE + config->dcc_ndl_chan_list_len +
		WMI_TLV_HDR_SIZE + active_state_count *
			sizeof(wmi_dcc_ndl_active_state_config) +
		WMI_TLV_HDR_SIZE + config->schedule_size *
			sizeof(wmi_ocb_schedule_element);
	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return -ENOMEM;
	}

	buf_ptr = (uint8_t *)wmi_buf_data(buf);
	cmd = (wmi_ocb_set_config_cmd_fixed_param *)buf_ptr;
	WMITLV_SET_HDR(&cmd->tlv_header,
		WMITLV_TAG_STRUC_wmi_ocb_set_config_cmd_fixed_param,
		WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_set_config_cmd_fixed_param));
	cmd->vdev_id = config->session_id;
	cmd->channel_count = config->channel_count;
	cmd->schedule_size = config->schedule_size;
	cmd->flags = config->flags;
	cmd->ta_max_duration = config->ta_max_duration;
	buf_ptr += sizeof(*cmd);

	/* Add the wmi_channel info */
	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		       config->channel_count*sizeof(wmi_channel));
	buf_ptr += WMI_TLV_HDR_SIZE;
	for (i = 0; i < config->channel_count; i++) {
		chan = (wmi_channel *)buf_ptr;
		WMITLV_SET_HDR(&chan->tlv_header,
				WMITLV_TAG_STRUC_wmi_channel,
				WMITLV_GET_STRUCT_TLVLEN(wmi_channel));

		chan->mhz = config->channels[i].chan_freq;
		chan->band_center_freq1 = config->channels[i].chan_freq;
		chan->band_center_freq2 = 0;
		chan->info = 0;

		WMI_SET_CHANNEL_MODE(chan, wma_ocb_freq_to_mode(chan->mhz));
		WMI_SET_CHANNEL_MAX_POWER(chan, config->channels[i].max_pwr);
		WMI_SET_CHANNEL_MIN_POWER(chan, config->channels[i].min_pwr);
		WMI_SET_CHANNEL_MAX_TX_POWER(chan, config->channels[i].max_pwr);
		WMI_SET_CHANNEL_REG_POWER(chan, config->channels[i].reg_pwr);
		WMI_SET_CHANNEL_ANTENNA_MAX(chan,
					    config->channels[i].antenna_max);

		if (config->channels[i].bandwidth < 10)
			WMI_SET_CHANNEL_FLAG(chan, WMI_CHAN_FLAG_QUARTER_RATE);
		else if (config->channels[i].bandwidth < 20)
			WMI_SET_CHANNEL_FLAG(chan, WMI_CHAN_FLAG_HALF_RATE);
		buf_ptr += sizeof(*chan);
	}

	/* Add the wmi_ocb_channel info */
	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		       config->channel_count*sizeof(wmi_ocb_channel));
	buf_ptr += WMI_TLV_HDR_SIZE;
	for (i = 0; i < config->channel_count; i++) {
		ocb_chan = (wmi_ocb_channel *)buf_ptr;
		WMITLV_SET_HDR(&ocb_chan->tlv_header,
			       WMITLV_TAG_STRUC_wmi_ocb_channel,
			       WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_channel));
		ocb_chan->bandwidth = config->channels[i].bandwidth;
		WMI_CHAR_ARRAY_TO_MAC_ADDR(config->channels[i].mac_address,
					   &ocb_chan->mac_address);
		buf_ptr += sizeof(*ocb_chan);
	}

	/* Add the wmi_qos_parameter info */
	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		config->channel_count * sizeof(wmi_qos_parameter)*WLAN_MAX_AC);
	buf_ptr += WMI_TLV_HDR_SIZE;
	/* WLAN_MAX_AC parameters for each channel */
	for (i = 0; i < config->channel_count; i++) {
		for (j = 0; j < WLAN_MAX_AC; j++) {
			qos_param = (wmi_qos_parameter *)buf_ptr;
			WMITLV_SET_HDR(&qos_param->tlv_header,
				WMITLV_TAG_STRUC_wmi_qos_parameter,
				WMITLV_GET_STRUCT_TLVLEN(wmi_qos_parameter));
			qos_param->aifsn =
				config->channels[i].qos_params[j].aifsn;
			qos_param->cwmin =
				config->channels[i].qos_params[j].cwmin;
			qos_param->cwmax =
				config->channels[i].qos_params[j].cwmax;
			buf_ptr += sizeof(*qos_param);
		}
	}

	/* Add the wmi_dcc_ndl_chan (per channel) */
	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		       config->dcc_ndl_chan_list_len);
	buf_ptr += WMI_TLV_HDR_SIZE;
	if (config->dcc_ndl_chan_list_len) {
		ndl_chan = (wmi_dcc_ndl_chan *)buf_ptr;
		vos_mem_copy(ndl_chan, config->dcc_ndl_chan_list,
			     config->dcc_ndl_chan_list_len);
		for (i = 0; i < config->channel_count; i++)
			WMITLV_SET_HDR(&(ndl_chan[i].tlv_header),
				WMITLV_TAG_STRUC_wmi_dcc_ndl_chan,
				WMITLV_GET_STRUCT_TLVLEN(wmi_dcc_ndl_chan));
		buf_ptr += config->dcc_ndl_chan_list_len;
	}

	/* Add the wmi_dcc_ndl_active_state_config */
	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC, active_state_count *
		       sizeof(wmi_dcc_ndl_active_state_config));
	buf_ptr += WMI_TLV_HDR_SIZE;
	if (active_state_count) {
		ndl_active_config = (wmi_dcc_ndl_active_state_config *)buf_ptr;
		vos_mem_copy(ndl_active_config,
			config->dcc_ndl_active_state_list,
			active_state_count * sizeof(*ndl_active_config));
		for (i = 0; i < active_state_count; ++i)
			WMITLV_SET_HDR(&(ndl_active_config[i].tlv_header),
			  WMITLV_TAG_STRUC_wmi_dcc_ndl_active_state_config,
			  WMITLV_GET_STRUCT_TLVLEN(
				wmi_dcc_ndl_active_state_config));
		buf_ptr += active_state_count *
			sizeof(*ndl_active_config);
	}

	/* Add the wmi_ocb_schedule_element info */
	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		config->schedule_size * sizeof(wmi_ocb_schedule_element));
	buf_ptr += WMI_TLV_HDR_SIZE;
	for (i = 0; i < config->schedule_size; i++) {
		sched_elem = (wmi_ocb_schedule_element *)buf_ptr;
		WMITLV_SET_HDR(&sched_elem->tlv_header,
			WMITLV_TAG_STRUC_wmi_ocb_schedule_element,
			WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_schedule_element));
		sched_elem->channel_freq = config->schedule[i].chan_freq;
		sched_elem->total_duration = config->schedule[i].total_duration;
		sched_elem->guard_interval = config->schedule[i].guard_interval;
		buf_ptr += sizeof(*sched_elem);
	}

	/*
	 * Save the configuration so that it can be used in
	 * wma_ocb_set_config_event_handler.
	 */
	if (wma_handle->ocb_config_req != config) {
		if (wma_handle->ocb_config_req)
			vos_mem_free(wma_handle->ocb_config_req);
		wma_handle->ocb_config_req = sir_copy_sir_ocb_config(config);
	}

	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_OCB_SET_CONFIG_CMDID);
	if (ret != EOK) {
		if (wma_handle->ocb_config_req) {
			vos_mem_free(wma_handle->ocb_config_req);
			wma_handle->ocb_config_req = 0;
		}

		WMA_LOGE("Failed to set OCB config");
		wmi_buf_free(buf);
		return -EIO;
	}
	return 0;
}

/**
 * wma_ocb_set_config_event_handler() - Response event for the set config cmd
 * @handle: the WMA handle
 * @event_buf: buffer with the event parameters
 * @len: length of the buffer
 *
 * Return: 0 on success
 */
int wma_ocb_set_config_event_handler(void *handle, uint8_t *event_buf,
				     uint32_t len)
{
	WMI_OCB_SET_CONFIG_RESP_EVENTID_param_tlvs *param_tlvs;
	wmi_ocb_set_config_resp_event_fixed_param *fix_param;
	param_tlvs = (WMI_OCB_SET_CONFIG_RESP_EVENTID_param_tlvs *)event_buf;
	fix_param = param_tlvs->fixed_param;
	return wma_ocb_set_config_resp(handle, fix_param->status);
};

/**
 * wma_ocb_set_utc_time() - send the UTC time to the firmware
 * @wma_handle: pointer to the WMA handle
 * @utc: pointer to the UTC time struct
 *
 * Return: 0 on succes
 */
int wma_ocb_set_utc_time(tp_wma_handle wma_handle, struct sir_ocb_utc *utc)
{
	int32_t ret;
	wmi_ocb_set_utc_time_cmd_fixed_param *cmd;
	uint8_t *buf_ptr;
	uint32_t len, i;
	wmi_buf_t buf;

	len = sizeof(*cmd);
	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return -ENOMEM;
	}

	buf_ptr = (uint8_t *)wmi_buf_data(buf);
	cmd = (wmi_ocb_set_utc_time_cmd_fixed_param *)buf_ptr;
	WMITLV_SET_HDR(&cmd->tlv_header,
		WMITLV_TAG_STRUC_wmi_ocb_set_utc_time_cmd_fixed_param,
		WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_set_utc_time_cmd_fixed_param));
	cmd->vdev_id = utc->vdev_id;

	for (i = 0; i < SIZE_UTC_TIME; i++)
		WMI_UTC_TIME_SET(cmd, i, utc->utc_time[i]);

	for (i = 0; i < SIZE_UTC_TIME_ERROR; i++)
		WMI_TIME_ERROR_SET(cmd, i, utc->time_error[i]);

	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_OCB_SET_UTC_TIME_CMDID);
	if (ret != EOK) {
		WMA_LOGE(FL("Failed to set OCB UTC time"));
		wmi_buf_free(buf);
		return -EIO;
	}

	return 0;
}

/**
 * wma_ocb_start_timing_advert() - start sending the timing advertisement
 *				   frames on a channel
 * @wma_handle: pointer to the WMA handle
 * @timing_advert: pointer to the timing advertisement struct
 *
 * Return: 0 on succes
 */
int wma_ocb_start_timing_advert(tp_wma_handle wma_handle,
	struct sir_ocb_timing_advert *timing_advert)
{
	int32_t ret;
	wmi_ocb_start_timing_advert_cmd_fixed_param *cmd;
	uint8_t *buf_ptr;
	uint32_t len, len_template;
	wmi_buf_t buf;

	len = sizeof(*cmd) +
		     WMI_TLV_HDR_SIZE;

	len_template = timing_advert->template_length;
	/* Add padding to the template if needed */
	if (len_template % 4 != 0)
		len_template += 4 - (len_template % 4);
	len += len_template;

	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return -ENOMEM;
	}

	buf_ptr = (uint8_t *)wmi_buf_data(buf);
	cmd = (wmi_ocb_start_timing_advert_cmd_fixed_param *)buf_ptr;
	WMITLV_SET_HDR(&cmd->tlv_header,
		WMITLV_TAG_STRUC_wmi_ocb_start_timing_advert_cmd_fixed_param,
		WMITLV_GET_STRUCT_TLVLEN(
			wmi_ocb_start_timing_advert_cmd_fixed_param));
	cmd->vdev_id = timing_advert->vdev_id;
	cmd->repeat_rate = timing_advert->repeat_rate;
	cmd->channel_freq = timing_advert->chan_freq;
	cmd->timestamp_offset = timing_advert->timestamp_offset;
	cmd->time_value_offset = timing_advert->time_value_offset;
	cmd->timing_advert_template_length = timing_advert->template_length;
	buf_ptr += sizeof(*cmd);

	/* Add the timing advert template */
	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE,
		       len_template);
	vos_mem_copy(buf_ptr + WMI_TLV_HDR_SIZE,
		     (uint8_t *)timing_advert->template_value,
		     timing_advert->template_length);

	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_OCB_START_TIMING_ADVERT_CMDID);
	if (ret != EOK) {
		WMA_LOGE(FL("Failed to start OCB timing advert"));
		wmi_buf_free(buf);
		return -EIO;
	}

	return 0;
}

/**
 * wma_ocb_stop_timing_advert() - stop sending the timing advertisement frames
 *				  on a channel
 * @wma_handle: pointer to the WMA handle
 * @timing_advert: pointer to the timing advertisement struct
 *
 * Return: 0 on succes
 */
int wma_ocb_stop_timing_advert(tp_wma_handle wma_handle,
	struct sir_ocb_timing_advert *timing_advert)
{
	int32_t ret;
	wmi_ocb_stop_timing_advert_cmd_fixed_param *cmd;
	uint8_t *buf_ptr;
	uint32_t len;
	wmi_buf_t buf;

	len = sizeof(*cmd);
	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return -ENOMEM;
	}

	buf_ptr = (uint8_t *)wmi_buf_data(buf);
	cmd = (wmi_ocb_stop_timing_advert_cmd_fixed_param *)buf_ptr;
	WMITLV_SET_HDR(&cmd->tlv_header,
		WMITLV_TAG_STRUC_wmi_ocb_stop_timing_advert_cmd_fixed_param,
		WMITLV_GET_STRUCT_TLVLEN(
			wmi_ocb_stop_timing_advert_cmd_fixed_param));
	cmd->vdev_id = timing_advert->vdev_id;
	cmd->channel_freq = timing_advert->chan_freq;

	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_OCB_STOP_TIMING_ADVERT_CMDID);
	if (ret != EOK) {
		WMA_LOGE(FL("Failed to stop OCB timing advert"));
		wmi_buf_free(buf);
		return -EIO;
	}

	return 0;
}

/**
 * wma_ocb_get_tsf_timer() - stop sending the timing advertisement frames on a
 *			     channel
 * @wma_handle: pointer to the WMA handle
 * @request: pointer to the request
 *
 * Return: 0 on succes
 */
int wma_ocb_get_tsf_timer(tp_wma_handle wma_handle,
			  struct sir_ocb_get_tsf_timer *request)
{
	VOS_STATUS ret;
	wmi_ocb_get_tsf_timer_cmd_fixed_param *cmd;
	uint8_t *buf_ptr;
	wmi_buf_t buf;
	int32_t len;

	len = sizeof(*cmd);
	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return -ENOMEM;
	}
	buf_ptr = (uint8_t *)wmi_buf_data(buf);

	cmd = (wmi_ocb_get_tsf_timer_cmd_fixed_param *)buf_ptr;
	vos_mem_zero(cmd, len);
	WMITLV_SET_HDR(&cmd->tlv_header,
		WMITLV_TAG_STRUC_wmi_ocb_get_tsf_timer_cmd_fixed_param,
		WMITLV_GET_STRUCT_TLVLEN(
			wmi_ocb_get_tsf_timer_cmd_fixed_param));
	cmd->vdev_id = request->vdev_id;

	/* Send the WMI command */
	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_OCB_GET_TSF_TIMER_CMDID);
	/* If there is an error, set the completion event */
	if (ret != EOK) {
		WMA_LOGE(FL("Failed to send WMI message: %d"), ret);
		wmi_buf_free(buf);
		return -EIO;
	}
	return 0;
}

/**
 * wma_ocb_get_tsf_timer_resp_event_handler() - Event for the get TSF timer cmd
 * @handle: the WMA handle
 * @event_buf: buffer with the event parameters
 * @len: length of the buffer
 *
 * Return: 0 on success
 */
int wma_ocb_get_tsf_timer_resp_event_handler(void *handle, uint8_t *event_buf,
					     uint32_t len)
{
	VOS_STATUS vos_status;
	struct sir_ocb_get_tsf_timer_response *response;
	WMI_OCB_GET_TSF_TIMER_RESP_EVENTID_param_tlvs *param_tlvs;
	wmi_ocb_get_tsf_timer_resp_event_fixed_param *fix_param;
	vos_msg_t msg = {0};

	param_tlvs = (WMI_OCB_GET_TSF_TIMER_RESP_EVENTID_param_tlvs *)event_buf;
	fix_param = param_tlvs->fixed_param;

	/* Allocate and populate the response */
	response = vos_mem_malloc(sizeof(*response));
	if (response == NULL)
		return -ENOMEM;
	response->vdev_id = fix_param->vdev_id;
	response->timer_high = fix_param->tsf_timer_high;
	response->timer_low = fix_param->tsf_timer_low;

	msg.type = eWNI_SME_OCB_GET_TSF_TIMER_RSP;
	msg.bodyptr = response;

	vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &msg);
	if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
		WMA_LOGE(FL("Failed to post msg to SME"));
		vos_mem_free(response);
		return -EINVAL;
	}

	return 0;
}

/**
 * wma_dcc_get_stats() - get the DCC channel stats
 * @wma_handle: pointer to the WMA handle
 * @get_stats_param: pointer to the dcc stats
 *
 * Return: 0 on succes
 */
int wma_dcc_get_stats(tp_wma_handle wma_handle,
		      struct sir_dcc_get_stats *get_stats_param)
{
	int32_t ret;
	wmi_dcc_get_stats_cmd_fixed_param *cmd;
	wmi_dcc_channel_stats_request *channel_stats_array;
	wmi_buf_t buf;
	uint8_t *buf_ptr;
	uint32_t len;
	uint32_t i;

	/* Validate the input */
	if (get_stats_param->request_array_len !=
	    get_stats_param->channel_count * sizeof(*channel_stats_array)) {
		WMA_LOGE(FL("Invalid parameter"));
		return -EINVAL;
	}

	/* Allocate memory for the WMI command */
	len = sizeof(*cmd) + WMI_TLV_HDR_SIZE +
		get_stats_param->request_array_len;

	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return VOS_STATUS_E_NOMEM;
	}

	buf_ptr = wmi_buf_data(buf);
	vos_mem_zero(buf_ptr, len);

	/* Populate the WMI command */
	cmd = (wmi_dcc_get_stats_cmd_fixed_param *)buf_ptr;
	buf_ptr += sizeof(*cmd);

	WMITLV_SET_HDR(&cmd->tlv_header,
		       WMITLV_TAG_STRUC_wmi_dcc_get_stats_cmd_fixed_param,
		       WMITLV_GET_STRUCT_TLVLEN(
			   wmi_dcc_get_stats_cmd_fixed_param));
	cmd->vdev_id = get_stats_param->vdev_id;
	cmd->num_channels = get_stats_param->channel_count;

	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		       get_stats_param->request_array_len);
	buf_ptr += WMI_TLV_HDR_SIZE;

	channel_stats_array = (wmi_dcc_channel_stats_request *)buf_ptr;
	vos_mem_copy(channel_stats_array, get_stats_param->request_array,
		     get_stats_param->request_array_len);
	for (i = 0; i < cmd->num_channels; i++)
		WMITLV_SET_HDR(&channel_stats_array[i].tlv_header,
			WMITLV_TAG_STRUC_wmi_dcc_channel_stats_request,
			WMITLV_GET_STRUCT_TLVLEN(
			    wmi_dcc_channel_stats_request));

	/* Send the WMI command */
	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_DCC_GET_STATS_CMDID);

	if (ret != EOK) {
		WMA_LOGE(FL("Failed to send WMI message: %d"), ret);
		wmi_buf_free(buf);
		return -EIO;
	}

	return 0;
}

/**
 * wma_dcc_get_stats_resp_event_handler() - Response event for the get stats cmd
 * @handle: the WMA handle
 * @event_buf: buffer with the event parameters
 * @len: length of the buffer
 *
 * Return: 0 on success
 */
int wma_dcc_get_stats_resp_event_handler(void *handle, uint8_t *event_buf,
				uint32_t len)
{
	VOS_STATUS vos_status;
	struct sir_dcc_get_stats_response *response;
	WMI_DCC_GET_STATS_RESP_EVENTID_param_tlvs *param_tlvs;
	wmi_dcc_get_stats_resp_event_fixed_param *fix_param;
	vos_msg_t msg = {0};

	param_tlvs = (WMI_DCC_GET_STATS_RESP_EVENTID_param_tlvs *)event_buf;
	fix_param = param_tlvs->fixed_param;

	/* Allocate and populate the response */
	if (fix_param->num_channels > ((WMA_SVC_MSG_MAX_SIZE -
	    sizeof(*fix_param)) / sizeof(wmi_dcc_ndl_stats_per_channel))) {
		WMA_LOGE("%s: too many channels:%d", __func__,
			fix_param->num_channels);
		VOS_ASSERT(0);
		return -EINVAL;
	}
	response = vos_mem_malloc(sizeof(*response) + fix_param->num_channels *
		sizeof(wmi_dcc_ndl_stats_per_channel));
	if (response == NULL)
		return -ENOMEM;

	response->vdev_id = fix_param->vdev_id;
	response->num_channels = fix_param->num_channels;
	response->channel_stats_array_len =
		fix_param->num_channels * sizeof(wmi_dcc_ndl_stats_per_channel);
	response->channel_stats_array = ((void *)response) + sizeof(*response);
	vos_mem_copy(response->channel_stats_array,
		     param_tlvs->stats_per_channel_list,
		     response->channel_stats_array_len);

	msg.type = eWNI_SME_DCC_GET_STATS_RSP;
	msg.bodyptr = response;

	vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &msg);
	if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
		WMA_LOGE(FL("Failed to post msg to SME"));
		vos_mem_free(response);
		return -EINVAL;
	}

	return 0;
}

/**
 * wma_dcc_clear_stats() - command to clear the DCC stats
 * @wma_handle: pointer to the WMA handle
 * @clear_stats_param: parameters to the command
 *
 * Return: 0 on succes
 */
int wma_dcc_clear_stats(tp_wma_handle wma_handle,
			struct sir_dcc_clear_stats *clear_stats_param)
{
	int32_t ret;
	wmi_dcc_clear_stats_cmd_fixed_param *cmd;
	wmi_buf_t buf;
	uint8_t *buf_ptr;
	uint32_t len;

	/* Allocate memory for the WMI command */
	len = sizeof(*cmd);

	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return -ENOMEM;
	}

	buf_ptr = wmi_buf_data(buf);
	vos_mem_zero(buf_ptr, len);

	/* Populate the WMI command */
	cmd = (wmi_dcc_clear_stats_cmd_fixed_param *)buf_ptr;

	WMITLV_SET_HDR(&cmd->tlv_header,
		       WMITLV_TAG_STRUC_wmi_dcc_clear_stats_cmd_fixed_param,
		       WMITLV_GET_STRUCT_TLVLEN(
			   wmi_dcc_clear_stats_cmd_fixed_param));
	cmd->vdev_id = clear_stats_param->vdev_id;
	cmd->dcc_stats_bitmap = clear_stats_param->dcc_stats_bitmap;

	/* Send the WMI command */
	ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_DCC_CLEAR_STATS_CMDID);
	if (ret != EOK) {
		WMA_LOGE(FL("Failed to send the WMI command"));
		wmi_buf_free(buf);
		return -EIO;
	}

	return 0;
}

/**
 * wma_dcc_update_ndl() - command to update the NDL data
 * @wma_handle: pointer to the WMA handle
 * @update_ndl_param: pointer to the request parameters
 *
 * Return: 0 on success
 */
int wma_dcc_update_ndl(tp_wma_handle wma_handle,
		       struct sir_dcc_update_ndl *update_ndl_param)
{
	VOS_STATUS vos_status;
	wmi_dcc_update_ndl_cmd_fixed_param *cmd;
	wmi_dcc_ndl_chan *ndl_chan_array;
	wmi_dcc_ndl_active_state_config *ndl_active_state_array;
	uint32_t active_state_count;
	wmi_buf_t buf;
	uint8_t *buf_ptr;
	uint32_t len;
	uint32_t i;

	/* validate the input */
	if (update_ndl_param->dcc_ndl_chan_list_len !=
	    update_ndl_param->channel_count * sizeof(*ndl_chan_array)) {
		WMA_LOGE(FL("Invalid parameter"));
		return VOS_STATUS_E_INVAL;
	}
	active_state_count = 0;
	ndl_chan_array = update_ndl_param->dcc_ndl_chan_list;
	for (i = 0; i < update_ndl_param->channel_count; i++)
		active_state_count +=
			WMI_NDL_NUM_ACTIVE_STATE_GET(&ndl_chan_array[i]);
	if (update_ndl_param->dcc_ndl_active_state_list_len !=
	    active_state_count * sizeof(*ndl_active_state_array)) {
		WMA_LOGE(FL("Invalid parameter"));
		return VOS_STATUS_E_INVAL;
	}

	/* Allocate memory for the WMI command */
	len = sizeof(*cmd) +
		WMI_TLV_HDR_SIZE + update_ndl_param->dcc_ndl_chan_list_len +
		WMI_TLV_HDR_SIZE +
		update_ndl_param->dcc_ndl_active_state_list_len;

	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("wmi_buf_alloc failed"));
		return -ENOMEM;
	}

	buf_ptr = wmi_buf_data(buf);
	vos_mem_zero(buf_ptr, len);

	/* Populate the WMI command */
	cmd = (wmi_dcc_update_ndl_cmd_fixed_param *)buf_ptr;
	buf_ptr += sizeof(*cmd);

	WMITLV_SET_HDR(&cmd->tlv_header,
		       WMITLV_TAG_STRUC_wmi_dcc_update_ndl_cmd_fixed_param,
		       WMITLV_GET_STRUCT_TLVLEN(
			   wmi_dcc_update_ndl_cmd_fixed_param));
	cmd->vdev_id = update_ndl_param->vdev_id;
	cmd->num_channel = update_ndl_param->channel_count;

	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		       update_ndl_param->dcc_ndl_chan_list_len);
	buf_ptr += WMI_TLV_HDR_SIZE;

	ndl_chan_array = (wmi_dcc_ndl_chan *)buf_ptr;
	vos_mem_copy(ndl_chan_array, update_ndl_param->dcc_ndl_chan_list,
		     update_ndl_param->dcc_ndl_chan_list_len);
	for (i = 0; i < cmd->num_channel; i++)
		WMITLV_SET_HDR(&ndl_chan_array[i].tlv_header,
			WMITLV_TAG_STRUC_wmi_dcc_ndl_chan,
			WMITLV_GET_STRUCT_TLVLEN(
			    wmi_dcc_ndl_chan));
	buf_ptr += update_ndl_param->dcc_ndl_chan_list_len;

	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
		       update_ndl_param->dcc_ndl_active_state_list_len);
	buf_ptr += WMI_TLV_HDR_SIZE;

	ndl_active_state_array = (wmi_dcc_ndl_active_state_config *) buf_ptr;
	vos_mem_copy(ndl_active_state_array,
		     update_ndl_param->dcc_ndl_active_state_list,
		     update_ndl_param->dcc_ndl_active_state_list_len);
	for (i = 0; i < active_state_count; i++) {
		WMITLV_SET_HDR(&ndl_active_state_array[i].tlv_header,
			WMITLV_TAG_STRUC_wmi_dcc_ndl_active_state_config,
			WMITLV_GET_STRUCT_TLVLEN(
			    wmi_dcc_ndl_active_state_config));
	}
	buf_ptr += update_ndl_param->dcc_ndl_active_state_list_len;

	/* Send the WMI command */
	vos_status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				   WMI_DCC_UPDATE_NDL_CMDID);
	/* If there is an error, set the completion event */
	if (vos_status) {
		WMA_LOGE(FL("Failed to send WMI message: %d"), vos_status);
		wmi_buf_free(buf);
		return -EIO;
	}

	return 0;
}

/**
 * wma_dcc_update_ndl_resp_event_handler() - Response event for the update NDL
 * command
 * @handle: the WMA handle
 * @event_buf: buffer with the event parameters
 * @len: length of the buffer
 *
 * Return: 0 on success
 */
int wma_dcc_update_ndl_resp_event_handler(void *handle, uint8_t *event_buf,
					  uint32_t len)
{
	VOS_STATUS vos_status;
	struct sir_dcc_update_ndl_response *resp;
	WMI_DCC_UPDATE_NDL_RESP_EVENTID_param_tlvs *param_tlvs;
	wmi_dcc_update_ndl_resp_event_fixed_param *fix_param;
	vos_msg_t msg = {0};

	param_tlvs = (WMI_DCC_UPDATE_NDL_RESP_EVENTID_param_tlvs *)event_buf;
	fix_param = param_tlvs->fixed_param;
	/* Allocate and populate the response */
	resp = vos_mem_malloc(sizeof(*resp));
	if (!resp) {
		WMA_LOGE(FL("Error allocating memory for the response."));
		return -ENOMEM;
	}
	resp->vdev_id = fix_param->vdev_id;
	resp->status = fix_param->status;

	msg.type = eWNI_SME_DCC_UPDATE_NDL_RSP;
	msg.bodyptr = resp;

	vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &msg);
	if (!VOS_IS_STATUS_SUCCESS(vos_status))	{
		WMA_LOGE(FL("Failed to post msg to SME"));
		vos_mem_free(resp);
		return -EINVAL;
	}

	return 0;
}

/**
 * wma_dcc_stats_event_handler() - Response event for the get stats cmd
 * @handle: the WMA handle
 * @event_buf: buffer with the event parameters
 * @len: length of the buffer
 *
 * Return: 0 on success
 */
int wma_dcc_stats_event_handler(void *handle, uint8_t *event_buf,
				uint32_t len)
{
	VOS_STATUS vos_status;
	struct sir_dcc_get_stats_response *response;
	WMI_DCC_STATS_EVENTID_param_tlvs *param_tlvs;
	wmi_dcc_stats_event_fixed_param *fix_param;
	vos_msg_t msg = {0};

	param_tlvs = (WMI_DCC_STATS_EVENTID_param_tlvs *)event_buf;
	fix_param = param_tlvs->fixed_param;
	/* Allocate and populate the response */
	if (fix_param->num_channels > ((WMA_SVC_MSG_MAX_SIZE -
	    sizeof(*fix_param)) / sizeof(wmi_dcc_ndl_stats_per_channel))) {
		WMA_LOGE("%s: too many channels:%d", __func__,
			fix_param->num_channels);
		VOS_ASSERT(0);
		return -EINVAL;
	}

	if (fix_param->num_channels > param_tlvs->num_stats_per_channel_list) {
		WMA_LOGE("FW message num_chan %d more than TLV hdr %d",
			fix_param->num_channels,
			param_tlvs->num_stats_per_channel_list);
		return -EINVAL;
	}

	response = vos_mem_malloc(sizeof(*response) +
		fix_param->num_channels * sizeof(wmi_dcc_ndl_stats_per_channel));
	if (response == NULL)
		return -ENOMEM;
	response->vdev_id = fix_param->vdev_id;
	response->num_channels = fix_param->num_channels;
	response->channel_stats_array_len =
		fix_param->num_channels * sizeof(wmi_dcc_ndl_stats_per_channel);
	response->channel_stats_array = ((void *)response) + sizeof(*response);
	vos_mem_copy(response->channel_stats_array,
		     param_tlvs->stats_per_channel_list,
		     response->channel_stats_array_len);

	msg.type = eWNI_SME_DCC_STATS_EVENT;
	msg.bodyptr = response;

	vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &msg);
	if (!VOS_IS_STATUS_SUCCESS(vos_status))	{
		WMA_LOGE(FL("Failed to post msg to SME"));
		vos_mem_free(response);
		return -EINVAL;
	}

	return 0;
}

/**
 * wma_process_radio_chan_stats_req() - Send request radio chan stats to FW
 * @wma_handle: WMI handle
 * @req: radio channel
 *
 * This function is used to reads the incoming @req and fill in the
 * destination WMI structure, then send radio channel statistics request
 * command to FW.
 *
 * Return: 0 on success; error number otherwise.
 */
int wma_process_radio_chan_stats_req(tp_wma_handle wma_handle,
					struct radio_chan_stats_req *req)
{
	VOS_STATUS status;
	wmi_buf_t buf;
	wmi_request_radio_chan_stats_cmd_fixed_param *cmd;
	int len = sizeof(*cmd);

	buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
	if (!buf) {
		WMA_LOGE(FL("%s: wmi_buf_alloc failed"), __func__);
		return VOS_STATUS_E_NOMEM;
	}

	cmd = (wmi_request_radio_chan_stats_cmd_fixed_param *)wmi_buf_data(buf);
	WMITLV_SET_HDR(&cmd->tlv_header,
		WMITLV_TAG_STRUC_wmi_request_radio_chan_stats_cmd_fixed_param,
		WMITLV_GET_STRUCT_TLVLEN(
			wmi_request_radio_chan_stats_cmd_fixed_param));
	cmd->request_type = req->req_type;
	if (cmd->request_type == WMI_REQUEST_ONE_RADIO_CHAN_STATS)
		cmd->chan_mhz = req->chan_freq;
	cmd->reset_after_request = (A_UINT32)req->reset_after_req;

	status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
				      WMI_REQUEST_RADIO_CHAN_STATS_CMDID);
	if (status != EOK) {
		WMA_LOGE("Failed to send WMI_REQUEST_RADIO_CHAN_STATS_CMDID");
		wmi_buf_free(buf);
		return VOS_STATUS_E_FAILURE;
	}

	WMA_LOGI("Sent WMI_REQUEST_RADIO_CHAN_STATS_CMDID to FW");
	return 0;
}

/**
 * wma_radio_chan_stats_event_handler() - handle radio channel statistics event
 * @handle - pointer to wma handle.
 * @event - pointer to TLV info received in the event.
 * @evt_len - length of data in @event.
 *
 * This Function is a handler for firmware radio channel statistics event.
 *
 * Return: 0 on success, error number otherwise
 */
static int wma_radio_chan_stats_event_handler(void *handle, u_int8_t *event,
					      u_int32_t evt_len)
{
	uint8_t *buf;
	uint32_t i, len;
	VOS_STATUS vos_status;
	WMI_RADIO_CHAN_STATS_EVENTID_param_tlvs *param_tlvs;
	wmi_radio_chan_stats_event_fixed_param *fix_param;
	wmi_radio_chan_stats *chan_stats;
	struct radio_chan_stats_rsp *resp;
	vos_msg_t msg = { 0 };

	param_tlvs = (WMI_RADIO_CHAN_STATS_EVENTID_param_tlvs *) event;
	if (!param_tlvs) {
		WMA_LOGE("Invalid radio_chan_stats event buffer");
		return -EINVAL;
	}

	fix_param = param_tlvs->fixed_param;
	chan_stats = param_tlvs->radio_chan_stats;
	if (!chan_stats) {
		WMA_LOGE("Invalid radio_chan_stats ptr");
		return -EINVAL;
	}

	if (fix_param->num_chans > (UINT32_MAX - sizeof(*resp))/
	    sizeof(struct radio_chan_stats_info)) {
		WMA_LOGE("%s: number of channels = %d greater thanmax limit",
			  __func__,fix_param->num_chans);
		return -EINVAL;
	}

	len = sizeof(*resp) +
		fix_param->num_chans * sizeof(struct radio_chan_stats_info);
	buf = vos_mem_malloc(len);
	if (!buf)
		return -ENOMEM;

	resp = (struct radio_chan_stats_rsp *)buf;
	buf += sizeof(struct radio_chan_stats_rsp);
	resp->chan_stats = (struct radio_chan_stats_info *)buf;
	resp->num_chans = fix_param->num_chans;
	for (i = 0; i < resp->num_chans; i++) {
		resp->chan_stats[i].chan_freq = chan_stats[i].chan_mhz;
		resp->chan_stats[i].measurement_period =
					(uint64_t)chan_stats[i].measurement_period_us;
		resp->chan_stats[i].on_chan_us = (uint64_t)chan_stats[i].on_chan_us;
		resp->chan_stats[i].on_chan_ratio = chan_stats[i].on_chan_ratio;
		resp->chan_stats[i].tx_duration_us =
					(uint64_t)chan_stats[i].tx_duration_us;
		resp->chan_stats[i].rx_duration_us =
					(uint64_t)chan_stats[i].rx_duration_us;
		resp->chan_stats[i].chan_busy_ratio =
					chan_stats[i].chan_busy_ratio;
		resp->chan_stats[i].tx_mpdus = chan_stats[i].tx_mpdus;
		resp->chan_stats[i].tx_msdus = chan_stats[i].tx_msdus;
		resp->chan_stats[i].rx_succ_pkts = chan_stats[i].rx_succ_mpdus;
		resp->chan_stats[i].rx_fail_pkts = chan_stats[i].rx_fail_mpdus;
	}

	msg.type = eWNI_SME_RADIO_CHAN_STATS_IND;
	msg.bodyptr = resp;
	vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &msg);
	if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
		WMA_LOGE("Fail to post message to SME");
		vos_mem_free(resp);
		return vos_status;
	}

	return 0;
}

/**
 * wma_ocb_register_event_handlers() - register handlers for the OCB WMI
 * events
 * @wma_handle: pointer to the WMA handle
 *
 * Return: 0 on success, non-zero on failure
 */
int wma_ocb_register_event_handlers(tp_wma_handle wma_handle)
{
	int status;

	if (!wma_handle) {
		WMA_LOGE(FL("wma_handle is NULL"));
		return -EINVAL;
	}

	/* Initialize the members in WMA used by wma_ocb */
	status = wmi_unified_register_event_handler(wma_handle->wmi_handle,
			WMI_OCB_SET_CONFIG_RESP_EVENTID,
			wma_ocb_set_config_event_handler);
	if (status)
		return status;

	status = wmi_unified_register_event_handler(
			wma_handle->wmi_handle,
			WMI_OCB_GET_TSF_TIMER_RESP_EVENTID,
			wma_ocb_get_tsf_timer_resp_event_handler);
	if (status)
		return status;

	status = wmi_unified_register_event_handler(
			wma_handle->wmi_handle,
			WMI_DCC_GET_STATS_RESP_EVENTID,
			wma_dcc_get_stats_resp_event_handler);
	if (status)
		return status;

	status = wmi_unified_register_event_handler(
			wma_handle->wmi_handle,
			WMI_DCC_UPDATE_NDL_RESP_EVENTID,
			wma_dcc_update_ndl_resp_event_handler);
	if (status)
		return status;

	status = wmi_unified_register_event_handler(wma_handle->wmi_handle,
			WMI_DCC_STATS_EVENTID,
			wma_dcc_stats_event_handler);
	if (status)
		return status;

	status = wmi_unified_register_event_handler(wma_handle->wmi_handle,
			WMI_RADIO_CHAN_STATS_EVENTID,
			wma_radio_chan_stats_event_handler);
	if (status)
		return status;

	return VOS_STATUS_SUCCESS;
}
