qcacld-3.0: Fix radio_stats to handle num_radios more than 1

In case of multiple radio, driver need to wait until it receives
all the radios stats and tx power levels stats and then
post the radio stats to user space.

Change-Id: I6e4f9ac8f0d2950551301589dfd8332e8c349605
CRs-Fixed: 992365
diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c
index 777f1e0..b635702 100644
--- a/core/hdd/src/wlan_hdd_stats.c
+++ b/core/hdd/src/wlan_hdd_stats.c
@@ -776,6 +776,180 @@
 }
 
 /**
+ * hdd_llstats_radio_fill_channels() - radio stats fill channels
+ * @adapter: Pointer to device adapter
+ * @radiostat: Pointer to stats data
+ * @vendor_event: vendor event
+ *
+ * Return: 0 on success; errno on failure
+ */
+static int hdd_llstats_radio_fill_channels(hdd_adapter_t *adapter,
+					   tSirWifiRadioStat *radiostat,
+					   struct sk_buff *vendor_event)
+{
+	tSirWifiChannelStats *channel_stats;
+	struct nlattr *chlist;
+	struct nlattr *chinfo;
+	int i;
+
+	chlist = nla_nest_start(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO);
+	if (chlist == NULL) {
+		hdd_err("nla_nest_start failed");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < radiostat->numChannels; i++) {
+		channel_stats = (tSirWifiChannelStats *) ((uint8_t *)
+				     radiostat->channels +
+				     (i * sizeof(tSirWifiChannelStats)));
+
+		chinfo = nla_nest_start(vendor_event, i);
+		if (chinfo == NULL) {
+			hdd_err("nla_nest_start failed");
+			return -EINVAL;
+		}
+
+		if (nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH,
+				channel_stats->channel.width) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ,
+				channel_stats->channel.centerFreq) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0,
+				channel_stats->channel.centerFreq0) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1,
+				channel_stats->channel.centerFreq1) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME,
+				channel_stats->onTime) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME,
+				channel_stats->ccaBusyTime)) {
+			hdd_err("nla_put failed");
+			return -EINVAL;
+		}
+		nla_nest_end(vendor_event, chinfo);
+	}
+	nla_nest_end(vendor_event, chlist);
+
+	return 0;
+}
+
+/**
+ * hdd_llstats_post_radio_stats() - post radio stats
+ * @adapter: Pointer to device adapter
+ * @more_data: More data
+ * @radiostat: Pointer to stats data
+ * @num_radio: Number of radios
+ *
+ * Return: 0 on success; errno on failure
+ */
+static int hdd_llstats_post_radio_stats(hdd_adapter_t *adapter,
+					u32 more_data,
+					tSirWifiRadioStat *radiostat,
+					u32 num_radio)
+{
+	struct sk_buff *vendor_event;
+	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	int ret;
+
+	/*
+	 * Allocate a size of 4096 for the Radio stats comprising
+	 * sizeof (tSirWifiRadioStat) + numChannels * sizeof
+	 * (tSirWifiChannelStats).Each channel data is put with an
+	 * NL attribute.The size of 4096 is considered assuming that
+	 * number of channels shall not exceed beyond  60 with the
+	 * sizeof (tSirWifiChannelStats) being 24 bytes.
+	 */
+
+	vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(
+					hdd_ctx->wiphy,
+					LL_STATS_EVENT_BUF_SIZE);
+
+	if (!vendor_event) {
+		hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
+		return -ENOMEM;
+	}
+
+	if (nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_RADIO) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA,
+			more_data) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS,
+			num_radio) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID,
+			radiostat->radio) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME,
+			radiostat->onTime) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME,
+			radiostat->txTime) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME,
+			radiostat->rxTime) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN,
+			radiostat->onTimeScan) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD,
+			radiostat->onTimeNbd) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN,
+			radiostat->onTimeGscan) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN,
+			radiostat->onTimeRoamScan) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN,
+			radiostat->onTimePnoScan) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20,
+			radiostat->onTimeHs20) ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS,
+			radiostat->total_num_tx_power_levels)    ||
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS,
+			radiostat->numChannels)) {
+		hdd_err("QCA_WLAN_VENDOR_ATTR put fail");
+		goto failure;
+	}
+
+	if (radiostat->total_num_tx_power_levels) {
+		if (nla_put(vendor_event,
+			    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL,
+			    sizeof(u32) *
+			    radiostat->total_num_tx_power_levels,
+			    radiostat->tx_time_per_power_level)) {
+			hdd_err("nla_put fail");
+			goto failure;
+		}
+	}
+
+	if (radiostat->numChannels) {
+		ret = hdd_llstats_radio_fill_channels(adapter, radiostat,
+						      vendor_event);
+		if (ret)
+			goto failure;
+	}
+
+	cfg80211_vendor_cmd_reply(vendor_event);
+	return 0;
+
+failure:
+	kfree_skb(vendor_event);
+	return -EINVAL;
+}
+
+/**
  * hdd_link_layer_process_radio_stats() - This function is called after
  * @pAdapter: Pointer to device adapter
  * @more_data: More data
@@ -793,170 +967,45 @@
 					       tpSirWifiRadioStat pData,
 					       u32 num_radio)
 {
-	int status, i;
-	tpSirWifiRadioStat pWifiRadioStat;
-	tpSirWifiChannelStats pWifiChannelStats;
-	struct sk_buff *vendor_event;
+	int status, i, nr, ret;
+	tSirWifiRadioStat *pWifiRadioStat = pData;
 	hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
 
 	ENTER();
 
-	pWifiRadioStat = pData;
 	status = wlan_hdd_validate_context(pHddCtx);
 	if (0 != status)
 		return;
 
-	hdd_notice("LL_STATS_RADIO"
-	       " number of radios: %u radio: %d onTime: %u"
-	       " txTime: %u rxTime: %u onTimeScan: %u onTimeNbd: %u"
-	       " onTimeGscan: %u onTimeRoamScan: %u"
-	       " onTimePnoScan: %u  onTimeHs20: %u"
-	       " numChannels: %u total_num_tx_power_levels: %u",
-	       num_radio, pWifiRadioStat->radio,
-	       pWifiRadioStat->onTime, pWifiRadioStat->txTime,
-	       pWifiRadioStat->rxTime, pWifiRadioStat->onTimeScan,
-	       pWifiRadioStat->onTimeNbd, pWifiRadioStat->onTimeGscan,
-	       pWifiRadioStat->onTimeRoamScan,
-	       pWifiRadioStat->onTimePnoScan,
-	       pWifiRadioStat->onTimeHs20, pWifiRadioStat->numChannels,
-	       pWifiRadioStat->total_num_tx_power_levels);
+	hdd_notice("LL_STATS_RADIO: number of radios: %u", num_radio);
 
-	/*
-	 * Allocate a size of 4096 for the Radio stats comprising
-	 * sizeof (tSirWifiRadioStat) + numChannels * sizeof
-	 * (tSirWifiChannelStats).Each channel data is put with an
-	 * NL attribute.The size of 4096 is considered assuming that
-	 * number of channels shall not exceed beyond  60 with the
-	 * sizeof (tSirWifiChannelStats) being 24 bytes.
-	 */
-
-	vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
-				LL_STATS_EVENT_BUF_SIZE);
-
-	if (!vendor_event) {
-		hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
-		return;
+	for (i = 0; i < num_radio; i++) {
+		hdd_notice("LL_STATS_RADIO"
+		       " radio: %u onTime: %u txTime: %u rxTime: %u"
+		       " onTimeScan: %u onTimeNbd: %u"
+		       " onTimeGscan: %u onTimeRoamScan: %u"
+		       " onTimePnoScan: %u  onTimeHs20: %u"
+		       " numChannels: %u total_num_tx_pwr_levels: %u",
+		       pWifiRadioStat->radio, pWifiRadioStat->onTime,
+		       pWifiRadioStat->txTime, pWifiRadioStat->rxTime,
+		       pWifiRadioStat->onTimeScan, pWifiRadioStat->onTimeNbd,
+		       pWifiRadioStat->onTimeGscan,
+		       pWifiRadioStat->onTimeRoamScan,
+		       pWifiRadioStat->onTimePnoScan,
+		       pWifiRadioStat->onTimeHs20, pWifiRadioStat->numChannels,
+		       pWifiRadioStat->total_num_tx_power_levels);
+		pWifiRadioStat++;
 	}
 
-	if (nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_RADIO) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA,
-			more_data) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS,
-			num_radio) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID,
-			pWifiRadioStat->radio) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME,
-			pWifiRadioStat->onTime) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME,
-			pWifiRadioStat->txTime) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME,
-			pWifiRadioStat->rxTime) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN,
-			pWifiRadioStat->onTimeScan) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD,
-			pWifiRadioStat->onTimeNbd) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN,
-			pWifiRadioStat->onTimeGscan) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN,
-			pWifiRadioStat->onTimeRoamScan) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN,
-			pWifiRadioStat->onTimePnoScan) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20,
-			pWifiRadioStat->onTimeHs20) ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS,
-			pWifiRadioStat->total_num_tx_power_levels)    ||
-	    nla_put_u32(vendor_event,
-			QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS,
-			pWifiRadioStat->numChannels)) {
-		hdd_err("QCA_WLAN_VENDOR_ATTR put fail");
-
-		kfree_skb(vendor_event);
-		return;
-	}
-
-	if (pWifiRadioStat->total_num_tx_power_levels) {
-		if (nla_put(vendor_event,
-			    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL,
-			    sizeof(u32) *
-			    pWifiRadioStat->total_num_tx_power_levels,
-			    pWifiRadioStat->tx_time_per_power_level)) {
-			hdd_err("nla_put fail");
-			kfree_skb(vendor_event);
+	pWifiRadioStat = pData;
+	for (nr = 0; nr < num_radio; nr++) {
+		ret = hdd_llstats_post_radio_stats(pAdapter, more_data,
+						   pWifiRadioStat, num_radio);
+		if (ret)
 			return;
-		}
+
+		pWifiRadioStat++;
 	}
-
-	if (pWifiRadioStat->numChannels) {
-		struct nlattr *chList;
-		struct nlattr *chInfo;
-
-		chList = nla_nest_start(vendor_event,
-					QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO);
-		if (chList == NULL) {
-			hdd_err("nla_nest_start failed");
-			kfree_skb(vendor_event);
-			return;
-		}
-
-		for (i = 0; i < pWifiRadioStat->numChannels; i++) {
-			pWifiChannelStats = (tpSirWifiChannelStats) ((uint8_t *)
-								     pWifiRadioStat->
-								     channels +
-								     (i *
-								      sizeof
-								      (tSirWifiChannelStats)));
-
-			chInfo = nla_nest_start(vendor_event, i);
-			if (chInfo == NULL) {
-				hdd_err("nla_nest_start failed");
-				kfree_skb(vendor_event);
-				return;
-			}
-
-			if (nla_put_u32(vendor_event,
-					QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH,
-					pWifiChannelStats->channel.width) ||
-			    nla_put_u32(vendor_event,
-					QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ,
-					pWifiChannelStats->channel.centerFreq) ||
-			    nla_put_u32(vendor_event,
-					   QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0,
-					   pWifiChannelStats->channel.
-					   centerFreq0) ||
-			    nla_put_u32(vendor_event,
-					   QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1,
-					   pWifiChannelStats->channel.
-					   centerFreq1) ||
-			    nla_put_u32(vendor_event,
-					   QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME,
-					   pWifiChannelStats->onTime) ||
-			    nla_put_u32(vendor_event,
-					   QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME,
-					   pWifiChannelStats->ccaBusyTime)) {
-				hdd_err("nla_put failed");
-				kfree_skb(vendor_event);
-				return;
-			}
-			nla_nest_end(vendor_event, chInfo);
-		}
-		nla_nest_end(vendor_event, chList);
-	}
-	cfg80211_vendor_cmd_reply(vendor_event);
 	EXIT();
 	return;
 }
diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h
index 8ab3e32..5f98bcc 100644
--- a/core/mac/inc/sir_api.h
+++ b/core/mac/inc/sir_api.h
@@ -5046,7 +5046,7 @@
 	uint32_t tx_time_per_tpc[MAX_TPC_LEVELS];
 
 	/* channel statistics tSirWifiChannelStats */
-	tSirWifiChannelStats channels[0];
+	tSirWifiChannelStats *channels;
 } tSirWifiRadioStat, *tpSirWifiRadioStat;
 
 /* per rate statistics */
@@ -5238,6 +5238,7 @@
 	uint8_t ifaceId;
 	uint32_t rspId;
 	uint32_t moreResultToFollow;
+	uint32_t nr_received;
 	union {
 		uint32_t num_peers;
 		uint32_t num_radio;
diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c
index c8a6c6b..aeeb71b 100644
--- a/core/wma/src/wma_main.c
+++ b/core/wma/src/wma_main.c
@@ -2031,6 +2031,7 @@
 	wma_handle->enable_mc_list = cds_cfg->enable_mc_list;
 	wma_handle->bpf_packet_filter_enable =
 		cds_cfg->bpf_packet_filter_enable;
+	wma_handle->link_stats_results = NULL;
 #ifdef FEATURE_WLAN_RA_FILTERING
 	wma_handle->IsRArateLimitEnabled = cds_cfg->is_ra_ratelimit_enabled;
 	wma_handle->RArateLimitInterval = cds_cfg->ra_ratelimit_interval;
diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c
index 8c28ae1..f527385 100644
--- a/core/wma/src/wma_utils.c
+++ b/core/wma/src/wma_utils.c
@@ -610,6 +610,41 @@
 }
 
 /**
+ * wma_unified_radio_tx_mem_free() - Free radio tx power stats memory
+ * @handle: WMI handle
+ *
+ * Return: 0 on success, error number otherwise.
+ */
+static int wma_unified_radio_tx_mem_free(void *handle)
+{
+	tp_wma_handle wma_handle = (tp_wma_handle) handle;
+	tSirWifiRadioStat *rs_results;
+	uint32_t i = 0;
+
+	if (!wma_handle->link_stats_results)
+		return 0;
+
+	rs_results = (tSirWifiRadioStat *)&wma_handle->link_stats_results->results[0];
+	for (i = 0; i < wma_handle->link_stats_results->num_radio; i++) {
+		rs_results += i;
+		if (rs_results->tx_time_per_power_level) {
+			qdf_mem_free(rs_results->tx_time_per_power_level);
+			rs_results->tx_time_per_power_level = NULL;
+		}
+
+		if (rs_results->channels) {
+			qdf_mem_free(rs_results->channels);
+			rs_results->channels = NULL;
+		}
+	}
+
+	qdf_mem_free(wma_handle->link_stats_results);
+	wma_handle->link_stats_results = NULL;
+
+	return 0;
+}
+
+/**
  * wma_unified_radio_tx_power_level_stats_event_handler() - tx power level stats
  * @handle: WMI handle
  * @cmd_param_info: command param info
@@ -660,18 +695,21 @@
 		return -EINVAL;
 	}
 
-	rs_results = (tSirWifiRadioStat *) &link_stats_results->results[0];
-	tx_power_level_values = (uint8_t *) param_tlvs->tx_time_per_power_level;
-
-	WMA_LOGD("%s: total_num_tx_power_levels: %u num_tx_power_levels: %u power_level_offset: %u",
+	WMA_LOGD("%s: tot_num_tx_pwr_lvls: %u num_tx_pwr_lvls: %u pwr_lvl_offset: %u radio_id: %u",
 			__func__, fixed_param->total_num_tx_power_levels,
 			 fixed_param->num_tx_power_levels,
-			 fixed_param->power_level_offset);
+			 fixed_param->power_level_offset,
+			 fixed_param->radio_id);
+
+	rs_results = (tSirWifiRadioStat *) &link_stats_results->results[0] + fixed_param->radio_id;
+	tx_power_level_values = (uint8_t *) param_tlvs->tx_time_per_power_level;
 
 	rs_results->total_num_tx_power_levels =
 				fixed_param->total_num_tx_power_levels;
-	if (!rs_results->total_num_tx_power_levels)
+	if (!rs_results->total_num_tx_power_levels) {
+		link_stats_results->nr_received++;
 		goto post_stats;
+	}
 
 	if (!rs_results->tx_time_per_power_level) {
 		rs_results->tx_time_per_power_level = qdf_mem_malloc(
@@ -682,6 +720,7 @@
 			/* In error case, atleast send the radio stats without
 			 * tx_power_level stats */
 			rs_results->total_num_tx_power_levels = 0;
+			link_stats_results->nr_received++;
 			goto post_stats;
 		}
 	}
@@ -689,17 +728,26 @@
 		tx_power_level_values,
 		sizeof(uint32_t) * fixed_param->num_tx_power_levels);
 	if (rs_results->total_num_tx_power_levels ==
-	   (fixed_param->num_tx_power_levels + fixed_param->power_level_offset))
+	   (fixed_param->num_tx_power_levels + fixed_param->power_level_offset)) {
 		link_stats_results->moreResultToFollow = 0;
+		link_stats_results->nr_received++;
+	}
 
-	WMA_LOGD("%s: moreResultToFollow: %u",
-			__func__, link_stats_results->moreResultToFollow);
+	WMA_LOGD("%s: moreResultToFollow: %u nr: %u nr_received: %u",
+			__func__, link_stats_results->moreResultToFollow,
+			link_stats_results->num_radio,
+			link_stats_results->nr_received);
 
 	/* If still data to receive, return from here */
 	if (link_stats_results->moreResultToFollow)
 		return 0;
 
 post_stats:
+	if (link_stats_results->num_radio != link_stats_results->nr_received) {
+		/* Not received all radio stats yet, don't post yet */
+		return 0;
+	}
+
 	/* call hdd callback with Link Layer Statistics
 	 * vdev_id/ifacId in link_stats_results will be
 	 * used to retrieve the correct HDD context
@@ -707,11 +755,7 @@
 	mac->sme.pLinkLayerStatsIndCallback(mac->hHdd,
 		WMA_LINK_LAYER_STATS_RESULTS_RSP,
 		link_stats_results);
-	WMA_LOGD("%s: Radio Stats event posted to HDD", __func__);
-	qdf_mem_free(rs_results->tx_time_per_power_level);
-	rs_results->tx_time_per_power_level = NULL;
-	qdf_mem_free(wma_handle->link_stats_results);
-	wma_handle->link_stats_results = NULL;
+	wma_unified_radio_tx_mem_free(handle);
 
 	return 0;
 }
@@ -753,7 +797,6 @@
 		return -EINVAL;
 	}
 
-	WMA_LOGD("%s: Posting Radio Stats event to HDD", __func__);
 	param_tlvs = (WMI_RADIO_LINK_STATS_EVENTID_param_tlvs *) cmd_param_info;
 	if (!param_tlvs) {
 		WMA_LOGA("%s: Invalid stats event", __func__);
@@ -779,23 +822,26 @@
 	radio_stats_size = sizeof(tSirWifiRadioStat);
 	chan_stats_size = sizeof(tSirWifiChannelStats);
 	link_stats_results_size = sizeof(*link_stats_results) +
-				  radio_stats_size + (radio_stats->num_channels * chan_stats_size);
+				  fixed_param->num_radio * radio_stats_size;
 
-	wma_handle->link_stats_results = qdf_mem_malloc(link_stats_results_size);
-	if (NULL == wma_handle->link_stats_results) {
-		WMA_LOGD("%s: could not allocate mem for stats results-len %zu",
-			 __func__, link_stats_results_size);
-		return -ENOMEM;
+	if (!wma_handle->link_stats_results) {
+		wma_handle->link_stats_results = qdf_mem_malloc(link_stats_results_size);
+		if (NULL == wma_handle->link_stats_results) {
+			WMA_LOGD("%s: could not allocate mem for stats results-len %zu",
+				 __func__, link_stats_results_size);
+			return -ENOMEM;
+		}
 	}
+	link_stats_results = wma_handle->link_stats_results;
 
 	WMA_LOGD("Radio stats Fixed Param:");
-	WMA_LOGD("request_id %u num_radio %u more_radio_events %u",
+	WMA_LOGD("req_id: %u num_radio: %u more_radio_events: %u",
 		 fixed_param->request_id, fixed_param->num_radio,
 		 fixed_param->more_radio_events);
 
-	WMA_LOGD("Radio Info: radio_id %u on_time %u tx_time %u rx_time %u on_time_scan %u "
-		 "on_time_nbd %u on_time_gscan %u on_time_roam_scan %u "
-		 "on_time_pno_scan %u on_time_hs20 %u num_channels %u",
+	WMA_LOGD("Radio Info: radio_id: %u on_time: %u tx_time: %u rx_time: %u on_time_scan: %u "
+		 "on_time_nbd: %u on_time_gscan: %u on_time_roam_scan: %u "
+		 "on_time_pno_scan: %u on_time_hs20: %u num_channels: %u",
 		 radio_stats->radio_id, radio_stats->on_time,
 		 radio_stats->tx_time, radio_stats->rx_time,
 		 radio_stats->on_time_scan, radio_stats->on_time_nbd,
@@ -804,9 +850,6 @@
 		 radio_stats->on_time_pno_scan,
 		 radio_stats->on_time_hs20, radio_stats->num_channels);
 
-	link_stats_results = wma_handle->link_stats_results;
-	qdf_mem_zero(link_stats_results, link_stats_results_size);
-
 	link_stats_results->paramId = WMI_LINK_STATS_RADIO;
 	link_stats_results->rspId = fixed_param->request_id;
 	link_stats_results->ifaceId = 0;
@@ -827,7 +870,7 @@
 	t_radio_stats = (uint8_t *) radio_stats;
 	t_channel_stats = (uint8_t *) channel_stats;
 
-	rs_results = (tSirWifiRadioStat *) &results[0];
+	rs_results = (tSirWifiRadioStat *) &results[0] + radio_stats->radio_id;
 	rs_results->radio = radio_stats->radio_id;
 	rs_results->onTime = radio_stats->on_time;
 	rs_results->txTime = radio_stats->tx_time;
@@ -841,38 +884,57 @@
 	rs_results->total_num_tx_power_levels = 0;
 	rs_results->tx_time_per_power_level = NULL;
 	rs_results->numChannels = radio_stats->num_channels;
+	rs_results->channels = NULL;
 
-	chn_results = (tSirWifiChannelStats *) &rs_results->channels[0];
-	next_chan_offset = WMI_TLV_HDR_SIZE;
-	WMA_LOGD("Channel Stats Info");
-	for (count = 0; count < radio_stats->num_channels; count++) {
-		WMA_LOGD("channel_width %u center_freq %u center_freq0 %u "
-			 "center_freq1 %u radio_awake_time %u cca_busy_time %u",
-			 channel_stats->channel_width,
-			 channel_stats->center_freq,
-			 channel_stats->center_freq0,
-			 channel_stats->center_freq1,
-			 channel_stats->radio_awake_time,
-			 channel_stats->cca_busy_time);
-		channel_stats++;
+	if (rs_results->numChannels) {
+		rs_results->channels = (tSirWifiChannelStats *) qdf_mem_malloc(
+					radio_stats->num_channels *
+					chan_stats_size);
+		if (rs_results->channels == NULL) {
+			WMA_LOGD("%s: could not allocate mem for channel stats (size=%zu)",
+				 __func__, radio_stats->num_channels * chan_stats_size);
+			wma_unified_radio_tx_mem_free(handle);
+			return -ENOMEM;
+		}
 
-		qdf_mem_copy(chn_results,
-			     t_channel_stats + next_chan_offset,
-			     chan_stats_size);
-		chn_results++;
-		next_chan_offset += sizeof(*channel_stats);
+		chn_results = (tSirWifiChannelStats *) &rs_results->channels[0];
+		next_chan_offset = WMI_TLV_HDR_SIZE;
+		WMA_LOGD("Channel Stats Info");
+		for (count = 0; count < radio_stats->num_channels; count++) {
+			WMA_LOGD("channel_width %u center_freq %u center_freq0 %u "
+				 "center_freq1 %u radio_awake_time %u cca_busy_time %u",
+				 channel_stats->channel_width,
+				 channel_stats->center_freq,
+				 channel_stats->center_freq0,
+				 channel_stats->center_freq1,
+				 channel_stats->radio_awake_time,
+				 channel_stats->cca_busy_time);
+			channel_stats++;
+
+			qdf_mem_copy(chn_results,
+				     t_channel_stats + next_chan_offset,
+				     chan_stats_size);
+			chn_results++;
+			next_chan_offset += sizeof(*channel_stats);
+		}
 	}
 
 	if (link_stats_results->moreResultToFollow) {
 		/* More results coming, don't post yet */
 		return 0;
+	} else {
+		link_stats_results->nr_received++;
+	}
+
+	if (link_stats_results->num_radio != link_stats_results->nr_received) {
+		/* Not received all radio stats yet, don't post yet */
+		return 0;
 	}
 
 	pMac->sme.pLinkLayerStatsIndCallback(pMac->hHdd,
 					     WMA_LINK_LAYER_STATS_RESULTS_RSP,
 					     link_stats_results);
-	qdf_mem_free(wma_handle->link_stats_results);
-	wma_handle->link_stats_results = NULL;
+	wma_unified_radio_tx_mem_free(handle);
 
 	return 0;
 }