Эх сурвалжийг харах

qcacmn: Add RX handling for RHINE architecture

RHINE is soft UMAC based architecture which is not having
REO block, all the REO functionality will be implemented
in F.W and host level. Host will get the RX packets in
CE-RX rings in HTT format, to reap RX packets new HTT
messages will be extracted and parsed.

So implement RX handling based on new softumac architecture for RHINE.

Change-Id: If430dd017309e2b2a3eb5e27e1d8b58696abceb4
CRs-Fixed: 3382920
Karthik Kantamneni 2 жил өмнө
parent
commit
2edb0c0388

+ 16 - 1
dp/wifi3.0/rh/dp_rh.c

@@ -72,11 +72,17 @@ static QDF_STATUS dp_soc_detach_rh(struct dp_soc *soc)
 
 static QDF_STATUS dp_soc_init_rh(struct dp_soc *soc)
 {
+	/*Register RX offload flush handlers*/
+	hif_offld_flush_cb_register(soc->hif_handle, dp_rx_data_flush);
+
 	return QDF_STATUS_SUCCESS;
 }
 
 static QDF_STATUS dp_soc_deinit_rh(struct dp_soc *soc)
 {
+	/*Degister RX offload flush handlers*/
+	hif_offld_flush_cb_deregister(soc->hif_handle);
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -241,6 +247,10 @@ dp_rxdma_ring_sel_cfg_rh(struct dp_soc *soc)
 					    &htt_tlv_filter);
 		}
 	}
+
+	if (QDF_IS_STATUS_SUCCESS(status))
+		status = dp_htt_h2t_rx_ring_rfs_cfg(soc->htt_handle);
+
 	return status;
 }
 #else
@@ -322,6 +332,10 @@ dp_rxdma_ring_sel_cfg_rh(struct dp_soc *soc)
 					    &htt_tlv_filter);
 		}
 	}
+
+	if (QDF_IS_STATUS_SUCCESS(status))
+		status = dp_htt_h2t_rx_ring_rfs_cfg(soc->htt_handle);
+
 	return status;
 }
 #endif
@@ -425,7 +439,8 @@ void dp_initialize_arch_ops_rh(struct dp_arch_ops *arch_ops)
 	arch_ops->get_rx_hash_key = dp_get_rx_hash_key_rh;
 	arch_ops->dp_rx_desc_cookie_2_va =
 			dp_rx_desc_cookie_2_va_rh;
-	arch_ops->dp_rx_intrabss_handle_nawds = dp_rx_intrabss_handle_nawds_rh;
+	arch_ops->dp_rx_intrabss_mcast_handler =
+					dp_rx_intrabss_handle_nawds_rh;
 	arch_ops->dp_rx_word_mask_subscribe = dp_rx_word_mask_subscribe_rh;
 	arch_ops->dp_rxdma_ring_sel_cfg = dp_rxdma_ring_sel_cfg_rh;
 	arch_ops->dp_rx_peer_metadata_peer_id_get =

+ 100 - 0
dp/wifi3.0/rh/dp_rh_htt.c

@@ -52,6 +52,87 @@ dp_htt_h2t_send_complete_free_netbuf(
 	qdf_nbuf_free(netbuf);
 }
 
+QDF_STATUS dp_htt_h2t_rx_ring_rfs_cfg(struct htt_soc *soc)
+{
+	struct dp_htt_htc_pkt *pkt;
+	qdf_nbuf_t msg;
+	uint32_t *msg_word;
+	QDF_STATUS status;
+	uint8_t *htt_logger_bufp;
+
+	/*
+	 * TODO check do we need ini support in Evros
+	 * Receive flow steering configuration,
+	 * disable gEnableFlowSteering(=0) in ini if
+	 * FW doesn't support it
+	 */
+
+	/* reserve room for the HTC header */
+	msg = qdf_nbuf_alloc(soc->osdev,
+			     HTT_MSG_BUF_SIZE(HTT_RFS_CFG_REQ_BYTES),
+			     HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4,
+			     true);
+	if (!msg) {
+		dp_err("htt_msg alloc failed for RFS config");
+		return QDF_STATUS_E_NOMEM;
+	}
+	/*
+	 * Set the length of the message.
+	 * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added
+	 * separately during the below call to qdf_nbuf_push_head.
+	 * The contribution from the HTC header is added separately inside HTC.
+	 */
+	qdf_nbuf_put_tail(msg, HTT_RFS_CFG_REQ_BYTES);
+
+	/* fill in the message contents */
+	msg_word = (uint32_t *)qdf_nbuf_data(msg);
+
+	/* rewind beyond alignment pad to get to the HTC header reserved area */
+	qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING);
+
+	/* word 0 */
+	*msg_word = 0;
+	htt_logger_bufp = (uint8_t *)msg_word;
+	HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_RFS_CONFIG);
+	HTT_RX_RFS_CONFIG_SET(*msg_word, 1);
+
+	/*
+	 * TODO value should be obtained from ini maxMSDUsPerRxInd
+	 * currently this ini is legacy ol and available only from cds
+	 * make this ini common to HL and evros DP
+	 */
+	*msg_word |= ((32 & 0xff) << 16);
+
+	dp_htt_info("RFS sent to F.W: 0x%08x", *msg_word);
+
+	/*start*/
+	pkt = htt_htc_pkt_alloc(soc);
+	if (!pkt) {
+		qdf_nbuf_free(msg);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	pkt->soc_ctxt = NULL; /* not used during send-done callback */
+	SET_HTC_PACKET_INFO_TX(
+		&pkt->htc_pkt,
+		dp_htt_h2t_send_complete_free_netbuf,
+		qdf_nbuf_data(msg),
+		qdf_nbuf_len(msg),
+		soc->htc_endpoint,
+		HTC_TX_PACKET_TAG_RUNTIME_PUT); /* tag for no FW response msg */
+
+	SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg);
+	status = DP_HTT_SEND_HTC_PKT(soc, pkt, HTT_H2T_MSG_TYPE_RFS_CONFIG,
+				     htt_logger_bufp);
+
+	if (status != QDF_STATUS_SUCCESS) {
+		qdf_nbuf_free(msg);
+		htt_htc_pkt_free(soc, pkt);
+	}
+
+	return status;
+}
+
 static void
 dp_htt_rx_addba_handler_rh(struct dp_soc *soc, uint16_t peer_id,
 			   uint8_t tid, uint16_t win_sz)
@@ -101,6 +182,25 @@ dp_htt_t2h_msg_handler_fast(void *context, qdf_nbuf_t *cmpl_msdus,
 		switch (msg_type) {
 		case HTT_T2H_MSG_TYPE_RX_DATA_IND:
 		{
+			uint16_t vdev_id, msdu_cnt;
+			uint16_t peer_id, frag_ind;
+
+			peer_id = HTT_RX_DATA_IND_PEER_ID_GET(*msg_word);
+			frag_ind = HTT_RX_DATA_IND_FRAG_GET(*(msg_word + 1));
+			vdev_id = HTT_RX_DATA_IND_VDEV_ID_GET(*msg_word);
+
+			if (qdf_unlikely(frag_ind)) {
+				dp_rx_frag_indication_handler(soc->dp_soc,
+							      htt_t2h_msg,
+							      vdev_id, peer_id);
+				break;
+			}
+
+			msdu_cnt =
+				HTT_RX_DATA_IND_MSDU_CNT_GET(*(msg_word + 1));
+			dp_rx_data_indication_handler(soc->dp_soc, htt_t2h_msg,
+						      vdev_id, peer_id,
+						      msdu_cnt);
 			break;
 		}
 		/* TODO add support for TX completion handling */

+ 8 - 0
dp/wifi3.0/rh/dp_rh_htt.h

@@ -26,6 +26,14 @@
 #include "qdf_mem.h"
 #include "cdp_txrx_cmn_struct.h"
 
+/*
+ * dp_htt_h2t_rx_ring_rfs_cfg() - RFS config for RX DATA indication
+ * @htt_soc: Opaque htt SOC handle
+ *
+ * Return: QDF_STATUS success or failure
+ */
+QDF_STATUS dp_htt_h2t_rx_ring_rfs_cfg(struct htt_soc *soc);
+
 /*
  * dp_htt_soc_initialize_rh() - SOC level HTT initialization
  * @htt_soc: Opaque htt SOC handle

+ 934 - 0
dp/wifi3.0/rh/dp_rh_rx.c

@@ -20,7 +20,10 @@
 #include "hal_hw_headers.h"
 #include "dp_types.h"
 #include "dp_rx.h"
+#include "dp_tx.h"
+#include "dp_rx_defrag.h"
 #include "dp_rh_rx.h"
+#include "dp_rh_htt.h"
 #include "dp_peer.h"
 #include "hal_rx.h"
 #include "hal_rh_rx.h"
@@ -38,6 +41,63 @@
 #include "dp_rx_buffer_pool.h"
 #include "dp_rh.h"
 
+static inline uint8_t dp_rx_get_ctx_id_frm_napiid(uint8_t napi_id)
+{
+	/*
+	 * This is NAPI to CE then to rx context id mapping
+	 * example: CE1 is assigned with napi id 3(ce_id+1)
+	 * CE1 maps to RX context id 0, so napi id 2 maps to
+	 * RX context id 0, this need to optimized further.
+	 */
+	switch (napi_id) {
+	case 2:
+		return 0;
+	case 11:
+		return 1;
+	case 12:
+		return 2;
+	default:
+		dp_err("Invalid napi id, this should not happen");
+		qdf_assert_always(0);
+		break;
+	}
+	return 0;
+}
+
+void
+dp_rx_data_flush(void *data)
+{
+	struct qca_napi_info *napi_info = (struct qca_napi_info *)data;
+	uint8_t rx_ctx_id = dp_rx_get_ctx_id_frm_napiid(napi_info->id);
+	struct dp_soc *soc = cds_get_context(QDF_MODULE_ID_SOC);
+	struct dp_vdev *vdev;
+	int i;
+
+	if (rx_ctx_id == 0 && soc->rx.flags.defrag_timeout_check) {
+		uint32_t now_ms =
+			qdf_system_ticks_to_msecs(qdf_system_ticks());
+
+		if (now_ms >= soc->rx.defrag.next_flush_ms)
+			dp_rx_defrag_waitlist_flush(soc);
+	}
+
+	/*Get first available vdev to flush all RX packets across soc*/
+	for (i = 0; i < MAX_VDEV_CNT; i++) {
+		vdev = dp_vdev_get_ref_by_id(soc, i, DP_MOD_ID_RX);
+		if (vdev && vdev->osif_fisa_flush)
+			vdev->osif_fisa_flush(soc, rx_ctx_id);
+
+		if (vdev && vdev->osif_gro_flush) {
+			vdev->osif_gro_flush(vdev->osif_vdev,
+					     rx_ctx_id);
+			dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_RX);
+			return;
+		}
+		if (vdev)
+			dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_RX);
+	}
+}
+
 static inline
 bool is_sa_da_idx_valid(uint32_t max_ast,
 			qdf_nbuf_t nbuf, struct hal_rx_msdu_metadata msdu_info)
@@ -178,6 +238,880 @@ dp_rx_intrabss_fwd_rh(struct dp_soc *soc,
 	return false;
 }
 
+void
+dp_rx_data_indication_handler(struct dp_soc *soc, qdf_nbuf_t data_ind,
+			      uint16_t vdev_id, uint16_t peer_id,
+			      uint16_t msdu_count)
+{
+	uint8_t *data_ind_msg;
+	uint32_t *msg_word;
+	uint32_t rx_ctx_id;
+	hal_soc_handle_t hal_soc;
+	struct dp_rx_desc *rx_desc = NULL;
+	qdf_nbuf_t nbuf, next;
+	union dp_rx_desc_list_elem_t *head[MAX_PDEV_CNT];
+	union dp_rx_desc_list_elem_t *tail[MAX_PDEV_CNT];
+	uint32_t num_pending = msdu_count;
+	uint32_t rx_buf_cookie;
+	uint16_t msdu_len = 0;
+	struct dp_txrx_peer *txrx_peer;
+	dp_txrx_ref_handle txrx_ref_handle = NULL;
+	struct dp_vdev *vdev;
+	uint32_t pkt_len = 0;
+	uint8_t *rx_tlv_hdr;
+	uint32_t rx_bufs_reaped[MAX_PDEV_CNT];
+	uint8_t mac_id = 0;
+	struct dp_pdev *rx_pdev;
+	struct dp_srng *dp_rxdma_srng;
+	struct rx_desc_pool *rx_desc_pool;
+	struct cdp_tid_rx_stats *tid_stats;
+	qdf_nbuf_t nbuf_head;
+	qdf_nbuf_t nbuf_tail;
+	qdf_nbuf_t deliver_list_head;
+	qdf_nbuf_t deliver_list_tail;
+	uint32_t num_rx_bufs_reaped = 0;
+	struct hif_opaque_softc *scn;
+	int32_t tid = 0;
+	bool is_prev_msdu_last = true;
+	uint32_t rx_ol_pkt_cnt = 0;
+	struct hal_rx_msdu_metadata msdu_metadata;
+	qdf_nbuf_t ebuf_head;
+	qdf_nbuf_t ebuf_tail;
+	uint8_t pkt_capture_offload = 0;
+	uint32_t old_tid;
+	uint32_t peer_ext_stats;
+	uint32_t dsf;
+	uint32_t max_ast;
+	uint64_t current_time = 0;
+	uint32_t error;
+
+	DP_HIST_INIT();
+
+	/* TODO implement dp_rx_dump_info_and_assert equivalent in RHINE */
+
+	qdf_assert_always(soc && msdu_count);
+	hal_soc = soc->hal_soc;
+	qdf_assert_always(hal_soc);
+
+	scn = soc->hif_handle;
+	dp_runtime_pm_mark_last_busy(soc);
+
+	/* reset local variables here to be re-used in the function */
+	nbuf_head = NULL;
+	nbuf_tail = NULL;
+	deliver_list_head = NULL;
+	deliver_list_tail = NULL;
+	txrx_peer = NULL;
+	vdev = NULL;
+	num_rx_bufs_reaped = 0;
+	ebuf_head = NULL;
+	ebuf_tail = NULL;
+
+	qdf_mem_zero(rx_bufs_reaped, sizeof(rx_bufs_reaped));
+	qdf_mem_zero(head, sizeof(head));
+	qdf_mem_zero(tail, sizeof(tail));
+	old_tid = 0xff;
+	dsf = 0;
+	peer_ext_stats = 0;
+	max_ast = 0;
+	rx_pdev = NULL;
+	tid_stats = NULL;
+
+	dp_pkt_get_timestamp(&current_time);
+
+	peer_ext_stats = wlan_cfg_is_peer_ext_stats_enabled(soc->wlan_cfg_ctx);
+	max_ast = wlan_cfg_get_max_ast_idx(soc->wlan_cfg_ctx);
+
+	data_ind_msg = qdf_nbuf_data(data_ind);
+	msg_word =
+		(uint32_t *)(data_ind_msg + HTT_RX_DATA_IND_HDR_SIZE);
+	rx_ctx_id =
+		dp_rx_get_ctx_id_frm_napiid(QDF_NBUF_CB_RX_CTX_ID(data_ind));
+
+	while (qdf_likely(num_pending)) {
+		error = HTT_RX_DATA_MSDU_INFO_ERROR_VALID_GET(*(msg_word + 3));
+		if (qdf_unlikely(error)) {
+			dp_rx_err("MSDU RX error encountered error:%u", error);
+			/* TODO handle error MSDU gracefully */
+			qdf_assert_always(0);
+		}
+
+		rx_buf_cookie =
+			HTT_RX_DATA_MSDU_INFO_SW_BUFFER_COOKIE_GET(*(msg_word + 1));
+		rx_desc = dp_rx_cookie_2_va_rxdma_buf(soc, rx_buf_cookie);
+		if (qdf_unlikely(!rx_desc && !rx_desc->nbuf &&
+				 !rx_desc->in_use)) {
+			dp_rx_err("Invalid RX descriptor");
+			qdf_assert_always(0);
+			/* TODO handle this if its valid case */
+		}
+
+		if (qdf_unlikely(!dp_rx_desc_check_magic(rx_desc))) {
+			dp_err("Invalid rx_desc cookie=%d", rx_buf_cookie);
+			DP_STATS_INC(soc, rx.err.rx_desc_invalid_magic, 1);
+			qdf_assert(0);
+		}
+
+		msdu_len =
+			HTT_RX_DATA_MSDU_INFO_MSDU_LENGTH_GET(*(msg_word + 2));
+
+		if (qdf_unlikely(
+		HTT_RX_DATA_MSDU_INFO_MSDU_CONTINUATION_GET(*(msg_word + 2)))) {
+			/* previous msdu has end bit set, so current one is
+			 * the new MPDU
+			 */
+			if (is_prev_msdu_last) {
+				/* For new MPDU check if we can read complete
+				 * MPDU by comparing the number of buffers
+				 * available and number of buffers needed to
+				 * reap this MPDU
+				 */
+				if ((msdu_len /
+				     (RX_DATA_BUFFER_SIZE -
+				      soc->rx_pkt_tlv_size) + 1) >
+				    num_pending) {
+					DP_STATS_INC(soc,
+						     rx.msdu_scatter_wait_break,
+						     1);
+					/* This is not expected host cannot deal
+					 * with partial frame in single DATA
+					 * indication, F.W has to submit full
+					 * frame in single DATA indication
+					 */
+					qdf_assert_always(0);
+				}
+				is_prev_msdu_last = false;
+			}
+		}
+
+		if (HTT_RX_DATA_MSDU_INFO_MPDU_RETRY_BIT_GET(*(msg_word + 2)))
+			qdf_nbuf_set_rx_retry_flag(rx_desc->nbuf, 1);
+
+		if (HTT_RX_DATA_MSDU_INFO_RAW_MPDU_FRAME_GET(*(msg_word + 2)))
+			qdf_nbuf_set_raw_frame(rx_desc->nbuf, 1);
+
+		if (!is_prev_msdu_last &&
+		    HTT_RX_DATA_MSDU_INFO_LAST_MSDU_IN_MPDU_GET(*(msg_word + 2)))
+			is_prev_msdu_last = true;
+
+		rx_bufs_reaped[rx_desc->pool_id]++;
+		QDF_NBUF_CB_RX_PEER_ID(rx_desc->nbuf) = peer_id;
+		QDF_NBUF_CB_RX_VDEV_ID(rx_desc->nbuf) = vdev_id;
+
+		/*
+		 * save msdu flags first, last and continuation msdu in
+		 * nbuf->cb, also save mcbc, is_da_valid, is_sa_valid and
+		 * length to nbuf->cb. This ensures the info required for
+		 * per pkt processing is always in the same cache line.
+		 * This helps in improving throughput for smaller pkt
+		 * sizes.
+		 */
+		if (HTT_RX_DATA_MSDU_INFO_FIRST_MSDU_IN_MPDU_GET(*(msg_word + 2)))
+			qdf_nbuf_set_rx_chfrag_start(rx_desc->nbuf, 1);
+
+		if (HTT_RX_DATA_MSDU_INFO_MSDU_CONTINUATION_GET(*(msg_word + 2)))
+			qdf_nbuf_set_rx_chfrag_cont(rx_desc->nbuf, 1);
+
+		if (HTT_RX_DATA_MSDU_INFO_LAST_MSDU_IN_MPDU_GET(*(msg_word + 2)))
+			qdf_nbuf_set_rx_chfrag_end(rx_desc->nbuf, 1);
+
+		if (HTT_RX_DATA_MSDU_INFO_DA_IS_MCBC_GET(*(msg_word + 2)))
+			qdf_nbuf_set_da_mcbc(rx_desc->nbuf, 1);
+
+		if (HTT_RX_DATA_MSDU_INFO_DA_IS_VALID_GET(*(msg_word + 2)))
+			qdf_nbuf_set_da_valid(rx_desc->nbuf, 1);
+
+		if (HTT_RX_DATA_MSDU_INFO_SA_IS_VALID_GET(*(msg_word + 2)))
+			qdf_nbuf_set_sa_valid(rx_desc->nbuf, 1);
+
+		qdf_nbuf_set_tid_val(rx_desc->nbuf,
+			HTT_RX_DATA_MSDU_INFO_TID_INFO_GET(*(msg_word + 2)));
+
+		/*
+		 * TODO add REO destination indication value in HTT
+		 * set reo dest indication
+		 * qdf_nbuf_set_rx_reo_dest_ind_or_sw_excpt(
+		 * rx_desc->nbuf,
+		 * HAL_RX_REO_MSDU_REO_DST_IND_GET(ring_desc));
+		 */
+
+		QDF_NBUF_CB_RX_PKT_LEN(rx_desc->nbuf) = msdu_len;
+
+		QDF_NBUF_CB_RX_CTX_ID(rx_desc->nbuf) = rx_ctx_id;
+
+		/*
+		 * TODO  move unmap after scattered msdu waiting break logic
+		 * in case double skb unmap happened.
+		 */
+		dp_rx_nbuf_unmap(soc, rx_desc, rx_ctx_id);
+		rx_desc->unmapped = 1;
+		DP_RX_PROCESS_NBUF(soc, nbuf_head, nbuf_tail, ebuf_head,
+				   ebuf_tail, rx_desc);
+
+		num_pending -= 1;
+
+		dp_rx_add_to_free_desc_list(&head[rx_desc->pool_id],
+					    &tail[rx_desc->pool_id], rx_desc);
+		num_rx_bufs_reaped++;
+
+		msg_word += HTT_RX_DATA_MSDU_INFO_SIZE >> 2;
+	}
+
+	dp_rx_per_core_stats_update(soc, rx_ctx_id, num_rx_bufs_reaped);
+
+	for (mac_id = 0; mac_id < MAX_PDEV_CNT; mac_id++) {
+		/*
+		 * continue with next mac_id if no pkts were reaped
+		 * from that pool
+		 */
+		if (!rx_bufs_reaped[mac_id])
+			continue;
+
+		dp_rxdma_srng = &soc->rx_refill_buf_ring[mac_id];
+
+		rx_desc_pool = &soc->rx_desc_buf[mac_id];
+
+		dp_rx_buffers_replenish_simple(soc, mac_id, dp_rxdma_srng,
+					       rx_desc_pool,
+					       rx_bufs_reaped[mac_id],
+					       &head[mac_id], &tail[mac_id]);
+	}
+
+	dp_verbose_debug("replenished %u\n", rx_bufs_reaped[0]);
+	/* Peer can be NULL is case of LFR */
+	if (qdf_likely(txrx_peer))
+		vdev = NULL;
+
+	/*
+	 * BIG loop where each nbuf is dequeued from global queue,
+	 * processed and queued back on a per vdev basis. These nbufs
+	 * are sent to stack as and when we run out of nbufs
+	 * or a new nbuf dequeued from global queue has a different
+	 * vdev when compared to previous nbuf.
+	 */
+	nbuf = nbuf_head;
+	while (nbuf) {
+		next = nbuf->next;
+
+		if (qdf_unlikely(dp_rx_is_raw_frame_dropped(nbuf))) {
+			nbuf = next;
+			DP_STATS_INC(soc, rx.err.raw_frm_drop, 1);
+			continue;
+		}
+
+		rx_tlv_hdr = qdf_nbuf_data(nbuf);
+		vdev_id = QDF_NBUF_CB_RX_VDEV_ID(nbuf);
+		peer_id =  QDF_NBUF_CB_RX_PEER_ID(nbuf);
+
+		/* Get TID from struct cb->tid_val, save to tid */
+		if (qdf_nbuf_is_rx_chfrag_start(nbuf)) {
+			tid = qdf_nbuf_get_tid_val(nbuf);
+			if (tid >= CDP_MAX_DATA_TIDS) {
+				DP_STATS_INC(soc, rx.err.rx_invalid_tid_err, 1);
+				dp_rx_nbuf_free(nbuf);
+				nbuf = next;
+				continue;
+			}
+		}
+
+		if (qdf_unlikely(!txrx_peer)) {
+			txrx_peer =
+			dp_rx_get_txrx_peer_and_vdev(soc, nbuf, peer_id,
+						     &txrx_ref_handle,
+						     pkt_capture_offload,
+						     &vdev,
+						     &rx_pdev, &dsf,
+						     &old_tid);
+			if (qdf_unlikely(!txrx_peer) || qdf_unlikely(!vdev)) {
+				nbuf = next;
+				continue;
+			}
+		} else if (txrx_peer && txrx_peer->peer_id != peer_id) {
+			dp_txrx_peer_unref_delete(txrx_ref_handle,
+						  DP_MOD_ID_RX);
+
+			txrx_peer =
+			dp_rx_get_txrx_peer_and_vdev(soc, nbuf, peer_id,
+						     &txrx_ref_handle,
+						     pkt_capture_offload,
+						     &vdev,
+						     &rx_pdev, &dsf,
+						     &old_tid);
+			if (qdf_unlikely(!txrx_peer) || qdf_unlikely(!vdev)) {
+				nbuf = next;
+				continue;
+			}
+		}
+
+		if (txrx_peer) {
+			QDF_NBUF_CB_DP_TRACE_PRINT(nbuf) = false;
+			qdf_dp_trace_set_track(nbuf, QDF_RX);
+			QDF_NBUF_CB_RX_DP_TRACE(nbuf) = 1;
+			QDF_NBUF_CB_RX_PACKET_TRACK(nbuf) =
+				QDF_NBUF_RX_PKT_DATA_TRACK;
+		}
+
+		/* when hlos tid override is enabled, save tid in
+		 * skb->priority
+		 */
+		if (qdf_unlikely(vdev->skip_sw_tid_classification &
+					DP_TXRX_HLOS_TID_OVERRIDE_ENABLED))
+			qdf_nbuf_set_priority(nbuf, tid);
+
+		DP_RX_TID_SAVE(nbuf, tid);
+		if (qdf_unlikely(dsf) || qdf_unlikely(peer_ext_stats) ||
+		    dp_rx_pkt_tracepoints_enabled())
+			qdf_nbuf_set_timestamp(nbuf);
+
+		if (qdf_likely(old_tid != tid)) {
+			tid_stats =
+		&rx_pdev->stats.tid_stats.tid_rx_stats[rx_ctx_id][tid];
+			old_tid = tid;
+		}
+
+		/*
+		 * Check if DMA completed -- msdu_done is the last bit
+		 * to be written
+		 */
+		if (qdf_likely(!qdf_nbuf_is_rx_chfrag_cont(nbuf))) {
+			if (qdf_unlikely(!hal_rx_attn_msdu_done_get_rh(
+								 rx_tlv_hdr))) {
+				dp_err_rl("MSDU DONE failure");
+				DP_STATS_INC(soc, rx.err.msdu_done_fail, 1);
+				hal_rx_dump_pkt_tlvs(hal_soc, rx_tlv_hdr,
+						     QDF_TRACE_LEVEL_INFO);
+				tid_stats->fail_cnt[MSDU_DONE_FAILURE]++;
+				qdf_assert(0);
+				dp_rx_nbuf_free(nbuf);
+				nbuf = next;
+				continue;
+			} else if (qdf_unlikely(hal_rx_attn_msdu_len_err_get_rh(
+								 rx_tlv_hdr))) {
+				DP_STATS_INC(soc, rx.err.msdu_len_err, 1);
+				dp_rx_nbuf_free(nbuf);
+				nbuf = next;
+				continue;
+			}
+		}
+
+		DP_HIST_PACKET_COUNT_INC(vdev->pdev->pdev_id);
+		/*
+		 * First IF condition:
+		 * This condition is valid when 802.11 fragemented
+		 * pkts reinjected back, even though this case is
+		 * not valid for Rhine keeping it for sanity, verify
+		 * and remove this first if condition based on test.
+		 * Second IF condition:
+		 * The below condition happens when an MSDU is spread
+		 * across multiple buffers. This can happen in two cases
+		 * 1. The nbuf size is smaller then the received msdu.
+		 *    ex: we have set the nbuf size to 2048 during
+		 *        nbuf_alloc. but we received an msdu which is
+		 *        2304 bytes in size then this msdu is spread
+		 *        across 2 nbufs.
+		 *
+		 * 2. AMSDUs when RAW mode is enabled.
+		 *    ex: 1st MSDU is in 1st nbuf and 2nd MSDU is spread
+		 *        across 1st nbuf and 2nd nbuf and last MSDU is
+		 *        spread across 2nd nbuf and 3rd nbuf.
+		 *
+		 * for these scenarios let us create a skb frag_list and
+		 * append these buffers till the last MSDU of the AMSDU
+		 * Third condition:
+		 * This is the most likely case, we receive 802.3 pkts
+		 * decapsulated by HW, here we need to set the pkt length.
+		 */
+		hal_rx_msdu_metadata_get(hal_soc, rx_tlv_hdr, &msdu_metadata);
+		if (qdf_unlikely(qdf_nbuf_is_frag(nbuf))) {
+			bool is_mcbc, is_sa_vld, is_da_vld;
+
+			is_mcbc = hal_rx_msdu_end_da_is_mcbc_get(soc->hal_soc,
+								 rx_tlv_hdr);
+			is_sa_vld =
+				hal_rx_msdu_end_sa_is_valid_get(soc->hal_soc,
+								rx_tlv_hdr);
+			is_da_vld =
+				hal_rx_msdu_end_da_is_valid_get(soc->hal_soc,
+								rx_tlv_hdr);
+
+			qdf_nbuf_set_da_mcbc(nbuf, is_mcbc);
+			qdf_nbuf_set_da_valid(nbuf, is_da_vld);
+			qdf_nbuf_set_sa_valid(nbuf, is_sa_vld);
+
+			qdf_nbuf_pull_head(nbuf, soc->rx_pkt_tlv_size);
+		} else if (qdf_nbuf_is_rx_chfrag_cont(nbuf)) {
+			msdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf);
+			nbuf = dp_rx_sg_create(soc, nbuf);
+			next = nbuf->next;
+
+			if (qdf_nbuf_is_raw_frame(nbuf)) {
+				DP_STATS_INC(vdev->pdev, rx_raw_pkts, 1);
+				DP_PEER_PER_PKT_STATS_INC_PKT(txrx_peer,
+							      rx.raw, 1,
+							      msdu_len);
+			} else {
+				dp_rx_nbuf_free(nbuf);
+				DP_STATS_INC(soc, rx.err.scatter_msdu, 1);
+				dp_info_rl("scatter msdu len %d, dropped",
+					   msdu_len);
+				nbuf = next;
+				continue;
+			}
+		} else {
+			msdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf);
+			pkt_len = msdu_len +
+				  msdu_metadata.l3_hdr_pad +
+				  soc->rx_pkt_tlv_size;
+
+			qdf_nbuf_set_pktlen(nbuf, pkt_len);
+			dp_rx_skip_tlvs(soc, nbuf, msdu_metadata.l3_hdr_pad);
+		}
+
+		dp_rx_send_pktlog(soc, rx_pdev, nbuf, QDF_TX_RX_STATUS_OK);
+
+		if (!dp_wds_rx_policy_check(rx_tlv_hdr, vdev, txrx_peer)) {
+			dp_rx_err("%pK: Policy Check Drop pkt", soc);
+			DP_PEER_PER_PKT_STATS_INC(txrx_peer,
+						  rx.policy_check_drop, 1);
+			tid_stats->fail_cnt[POLICY_CHECK_DROP]++;
+			/* Drop & free packet */
+			dp_rx_nbuf_free(nbuf);
+			/* Statistics */
+			nbuf = next;
+			continue;
+		}
+
+		/*
+		 * Drop non-EAPOL frames from unauthorized peer.
+		 */
+		if (qdf_likely(txrx_peer) &&
+		    qdf_unlikely(!txrx_peer->authorize) &&
+		    !qdf_nbuf_is_raw_frame(nbuf)) {
+			bool is_eapol = qdf_nbuf_is_ipv4_eapol_pkt(nbuf) ||
+					qdf_nbuf_is_ipv4_wapi_pkt(nbuf);
+
+			if (!is_eapol) {
+				DP_PEER_PER_PKT_STATS_INC(txrx_peer,
+							  rx.peer_unauth_rx_pkt_drop,
+							  1);
+				dp_rx_nbuf_free(nbuf);
+				nbuf = next;
+				continue;
+			}
+		}
+
+		if (soc->process_rx_status)
+			dp_rx_cksum_offload(vdev->pdev, nbuf, rx_tlv_hdr);
+
+		dp_rx_msdu_stats_update(soc, nbuf, rx_tlv_hdr, txrx_peer,
+					rx_ctx_id, tid_stats);
+
+		if (qdf_likely(vdev->rx_decap_type ==
+			       htt_cmn_pkt_type_ethernet)) {
+			/* Due to HW issue, sometimes we see that the sa_idx
+			 * and da_idx are invalid with sa_valid and da_valid
+			 * bits set
+			 *
+			 * in this case we also see that value of
+			 * sa_sw_peer_id is set as 0
+			 *
+			 * Drop the packet if sa_idx and da_idx OOB or
+			 * sa_sw_peerid is 0
+			 */
+			if (!is_sa_da_idx_valid(max_ast, nbuf,
+						msdu_metadata)) {
+				dp_rx_nbuf_free(nbuf);
+				nbuf = next;
+				DP_STATS_INC(soc, rx.err.invalid_sa_da_idx, 1);
+				continue;
+			}
+			if (qdf_unlikely(dp_rx_mec_check_wrapper(soc,
+								 txrx_peer,
+								 rx_tlv_hdr,
+								 nbuf))) {
+				/* this is a looped back MCBC pkt,drop it */
+				DP_PEER_PER_PKT_STATS_INC_PKT(txrx_peer,
+							      rx.mec_drop, 1,
+							      QDF_NBUF_CB_RX_PKT_LEN(nbuf));
+				dp_rx_nbuf_free(nbuf);
+				nbuf = next;
+				continue;
+			}
+			/* WDS Source Port Learning */
+			if (qdf_likely(vdev->wds_enabled))
+				dp_rx_wds_srcport_learn(soc,
+							rx_tlv_hdr,
+							txrx_peer,
+							nbuf,
+							msdu_metadata);
+
+			/* Intrabss-fwd */
+			if (dp_rx_check_ap_bridge(vdev))
+				if (dp_rx_intrabss_fwd_rh(soc, txrx_peer,
+							  rx_tlv_hdr,
+							  nbuf,
+							  msdu_metadata,
+							  tid_stats)) {
+					nbuf = next;
+					tid_stats->intrabss_cnt++;
+					continue; /* Get next desc */
+				}
+		}
+
+		dp_rx_fill_gro_info(soc, rx_tlv_hdr, nbuf, &rx_ol_pkt_cnt);
+
+		/*
+		 * TODO mark first packet after wow get this from HTT desc
+		 * dp_rx_mark_first_packet_after_wow_wakeup(vdev->pdev,
+		 * rx_tlv_hdr, nbuf);
+		 */
+
+		dp_rx_update_stats(soc, nbuf);
+
+		dp_pkt_add_timestamp(txrx_peer->vdev, QDF_PKT_RX_DRIVER_ENTRY,
+				     current_time, nbuf);
+
+		DP_RX_LIST_APPEND(deliver_list_head,
+				  deliver_list_tail,
+				  nbuf);
+		DP_PEER_STATS_FLAT_INC_PKT(txrx_peer, to_stack, 1,
+					   QDF_NBUF_CB_RX_PKT_LEN(nbuf));
+		if (qdf_unlikely(txrx_peer->in_twt))
+			DP_PEER_PER_PKT_STATS_INC_PKT(txrx_peer,
+						      rx.to_stack_twt, 1,
+						      QDF_NBUF_CB_RX_PKT_LEN(nbuf));
+
+		tid_stats->delivered_to_stack++;
+		nbuf = next;
+	}
+
+	DP_RX_DELIVER_TO_STACK(soc, vdev, txrx_peer, peer_id,
+			       pkt_capture_offload,
+			       deliver_list_head,
+			       deliver_list_tail);
+
+	if (qdf_likely(txrx_peer))
+		dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX);
+
+	if (vdev && vdev->osif_fisa_flush)
+		vdev->osif_fisa_flush(soc, rx_ctx_id);
+
+	if (vdev && vdev->osif_gro_flush && rx_ol_pkt_cnt) {
+		vdev->osif_gro_flush(vdev->osif_vdev,
+					rx_ctx_id);
+	}
+
+	/* Update histogram statistics by looping through pdev's */
+	DP_RX_HIST_STATS_PER_PDEV();
+}
+
+/*
+ * dp_rx_defrag_deliver_rh(): Deliver defrag packet to stack
+ * @peer: Pointer to the peer
+ * @tid: Transmit Identifier
+ * @head: Nbuf to be delivered
+ *
+ * Returns: None
+ */
+static inline void dp_rx_defrag_deliver_rh(struct dp_txrx_peer *txrx_peer,
+					   unsigned int tid,
+					   qdf_nbuf_t head)
+{
+	struct dp_vdev *vdev = txrx_peer->vdev;
+	struct dp_soc *soc = vdev->pdev->soc;
+	qdf_nbuf_t deliver_list_head = NULL;
+	qdf_nbuf_t deliver_list_tail = NULL;
+	uint8_t *rx_tlv_hdr;
+
+	rx_tlv_hdr = qdf_nbuf_data(head);
+
+	QDF_NBUF_CB_RX_VDEV_ID(head) = vdev->vdev_id;
+	qdf_nbuf_set_tid_val(head, tid);
+	qdf_nbuf_pull_head(head, soc->rx_pkt_tlv_size);
+
+	DP_RX_LIST_APPEND(deliver_list_head, deliver_list_tail,
+			  head);
+	dp_rx_deliver_to_stack(soc, vdev, txrx_peer, deliver_list_head,
+			       deliver_list_tail);
+}
+
+static
+QDF_STATUS dp_rx_defrag_store_fragment_rh(struct dp_soc *soc, qdf_nbuf_t frag)
+{
+	struct dp_rx_reorder_array_elem *rx_reorder_array_elem;
+	struct dp_pdev *pdev;
+	struct dp_txrx_peer *txrx_peer = NULL;
+	dp_txrx_ref_handle txrx_ref_handle = NULL;
+	uint16_t peer_id, tid;
+	uint8_t fragno, more_frag, all_frag_present = 0;
+	uint16_t rxseq;
+	QDF_STATUS status;
+	struct dp_rx_tid_defrag *rx_tid;
+	uint8_t mpdu_sequence_control_valid;
+	uint8_t mpdu_frame_control_valid;
+	uint8_t *rx_buf_start = qdf_nbuf_data(frag);
+	uint32_t msdu_len;
+
+	if (qdf_nbuf_len(frag) > 0) {
+		dp_rx_info("Dropping unexpected packet with skb_len: %d, data len: %d",
+			   (uint32_t)qdf_nbuf_len(frag), frag->data_len);
+		DP_STATS_INC(soc, rx.rx_frag_err_len_error, 1);
+		goto discard_frag;
+	}
+
+	msdu_len = QDF_NBUF_CB_RX_PKT_LEN(frag);
+	qdf_nbuf_set_pktlen(frag, (msdu_len + soc->rx_pkt_tlv_size));
+	qdf_nbuf_append_ext_list(frag, NULL, 0);
+
+	/* Check if the packet is from a valid peer */
+	peer_id = QDF_NBUF_CB_RX_PEER_ID(frag);
+	txrx_peer = dp_txrx_peer_get_ref_by_id(soc, peer_id, &txrx_ref_handle,
+					       DP_MOD_ID_RX);
+
+	if (!txrx_peer) {
+		/* We should not receive anything from unknown peer
+		 * however, that might happen while we are in the monitor mode.
+		 * We don't need to handle that here
+		 */
+		dp_rx_info_rl("Unknown peer with peer_id %d, dropping fragment",
+			      peer_id);
+		DP_STATS_INC(soc, rx.rx_frag_err_no_peer, 1);
+		goto discard_frag;
+	}
+
+	tid = qdf_nbuf_get_tid_val(frag);
+	if (tid >= DP_MAX_TIDS) {
+		dp_rx_info("TID out of bounds: %d", tid);
+		qdf_assert_always(0);
+		goto discard_frag;
+	}
+
+	mpdu_sequence_control_valid =
+		hal_rx_get_mpdu_sequence_control_valid(soc->hal_soc,
+						       rx_buf_start);
+
+	/* Invalid MPDU sequence control field, MPDU is of no use */
+	if (!mpdu_sequence_control_valid) {
+		dp_rx_err("Invalid MPDU seq control field, dropping MPDU");
+
+		qdf_assert(0);
+		goto discard_frag;
+	}
+
+	mpdu_frame_control_valid =
+		hal_rx_get_mpdu_frame_control_valid(soc->hal_soc,
+						    rx_buf_start);
+
+	/* Invalid frame control field */
+	if (!mpdu_frame_control_valid) {
+		dp_rx_err("Invalid frame control field, dropping MPDU");
+
+		qdf_assert(0);
+		goto discard_frag;
+	}
+
+	/* Current mpdu sequence */
+	more_frag = dp_rx_frag_get_more_frag_bit(soc, rx_buf_start);
+
+	/* HW does not populate the fragment number as of now
+	 * need to get from the 802.11 header
+	 */
+	fragno = dp_rx_frag_get_mpdu_frag_number(soc, rx_buf_start);
+	rxseq = dp_rx_frag_get_mpdu_seq_number(soc, rx_buf_start);
+
+	pdev = txrx_peer->vdev->pdev;
+	rx_tid = &txrx_peer->rx_tid[tid];
+
+	qdf_spin_lock_bh(&rx_tid->defrag_tid_lock);
+	rx_reorder_array_elem = txrx_peer->rx_tid[tid].array;
+	if (!rx_reorder_array_elem) {
+		dp_err_rl("Rcvd Fragmented pkt before tid setup for peer %pK",
+			  txrx_peer);
+		qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
+		goto discard_frag;
+	}
+
+	/*
+	 * !more_frag: no more fragments to be delivered
+	 * !frag_no: packet is not fragmented
+	 * !rx_reorder_array_elem->head: no saved fragments so far
+	 */
+	if (!more_frag && !fragno && !rx_reorder_array_elem->head) {
+		/* We should not get into this situation here.
+		 * It means an unfragmented packet with fragment flag
+		 * is delivered over frag indication.
+		 * Typically it follows normal rx path.
+		 */
+		dp_rx_err("Rcvd unfragmented pkt on fragmented path, dropping");
+
+		qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
+		qdf_assert(0);
+		goto discard_frag;
+	}
+
+	/* Check if the fragment is for the same sequence or a different one */
+	dp_rx_debug("rx_tid %d", tid);
+	if (rx_reorder_array_elem->head) {
+		dp_rx_debug("rxseq %d\n", rxseq);
+		if (rxseq != rx_tid->curr_seq_num) {
+			dp_rx_debug("mismatch cur_seq %d rxseq %d\n",
+				    rx_tid->curr_seq_num, rxseq);
+			/* Drop stored fragments if out of sequence
+			 * fragment is received
+			 */
+			dp_rx_reorder_flush_frag(txrx_peer, tid);
+
+			DP_STATS_INC(soc, rx.rx_frag_oor, 1);
+
+			dp_rx_debug("cur rxseq %d\n", rxseq);
+			/*
+			 * The sequence number for this fragment becomes the
+			 * new sequence number to be processed
+			 */
+			rx_tid->curr_seq_num = rxseq;
+		}
+	} else {
+		/* Check if we are processing first fragment if it is
+		 * not first fragment discard fragment.
+		 */
+		if (fragno) {
+			qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
+			goto discard_frag;
+		}
+		dp_rx_debug("cur rxseq %d\n", rxseq);
+		/* Start of a new sequence */
+		dp_rx_defrag_cleanup(txrx_peer, tid);
+		rx_tid->curr_seq_num = rxseq;
+	}
+
+	/*
+	 * If the earlier sequence was dropped, this will be the fresh start.
+	 * Else, continue with next fragment in a given sequence
+	 */
+	status = dp_rx_defrag_fraglist_insert(txrx_peer, tid,
+					      &rx_reorder_array_elem->head,
+					      &rx_reorder_array_elem->tail,
+					      frag, &all_frag_present);
+
+	if (pdev->soc->rx.flags.defrag_timeout_check)
+		dp_rx_defrag_waitlist_remove(txrx_peer, tid);
+
+	/* Yet to receive more fragments for this sequence number */
+	if (!all_frag_present) {
+		uint32_t now_ms =
+			qdf_system_ticks_to_msecs(qdf_system_ticks());
+
+		txrx_peer->rx_tid[tid].defrag_timeout_ms =
+			now_ms + pdev->soc->rx.defrag.timeout_ms;
+
+		if (pdev->soc->rx.flags.defrag_timeout_check)
+			dp_rx_defrag_waitlist_add(txrx_peer, tid);
+		dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX_ERR);
+		qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
+
+		return QDF_STATUS_SUCCESS;
+	}
+
+	dp_rx_debug("All fragments received for sequence: %d", rxseq);
+
+	/* Process the fragments */
+	status = dp_rx_defrag(txrx_peer, tid, rx_reorder_array_elem->head,
+			      rx_reorder_array_elem->tail);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		dp_rx_err("Fragment processing failed");
+
+		dp_rx_defrag_cleanup(txrx_peer, tid);
+		qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
+		goto end;
+	}
+
+	dp_rx_defrag_deliver_rh(txrx_peer, tid, rx_reorder_array_elem->head);
+	dp_rx_debug("Fragmented sequence successfully reinjected");
+
+	dp_rx_defrag_cleanup(txrx_peer, tid);
+	qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
+
+	dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX_ERR);
+
+	return QDF_STATUS_SUCCESS;
+
+discard_frag:
+	dp_rx_nbuf_free(frag);
+end:
+	if (txrx_peer)
+		dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX_ERR);
+
+	DP_STATS_INC(soc, rx.rx_frag_err, 1);
+	return QDF_STATUS_E_DEFRAG_ERROR;
+}
+
+void
+dp_rx_frag_indication_handler(struct dp_soc *soc, qdf_nbuf_t data_ind,
+			      uint16_t vdev_id, uint16_t peer_id)
+{
+	uint8_t *data_ind_msg;
+	uint32_t *msg_word;
+	uint32_t rx_ctx_id;
+	qdf_nbuf_t nbuf;
+	union dp_rx_desc_list_elem_t *head = NULL;
+	union dp_rx_desc_list_elem_t *tail = NULL;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	uint32_t rx_buf_cookie;
+	struct dp_rx_desc *rx_desc;
+	uint8_t mac_id = 0;
+
+	qdf_assert(soc);
+
+	data_ind_msg = qdf_nbuf_data(data_ind);
+	msg_word =
+		(uint32_t *)(data_ind_msg + HTT_RX_DATA_IND_HDR_SIZE);
+	rx_ctx_id =
+		dp_rx_get_ctx_id_frm_napiid(QDF_NBUF_CB_RX_CTX_ID(data_ind));
+
+	rx_buf_cookie =
+		HTT_RX_DATA_MSDU_INFO_SW_BUFFER_COOKIE_GET(*(msg_word + 1));
+	rx_desc = dp_rx_cookie_2_va_rxdma_buf(soc, rx_buf_cookie);
+	if (qdf_unlikely(!rx_desc && !rx_desc->nbuf &&
+			 !rx_desc->in_use)) {
+		dp_rx_err("Invalid RX descriptor");
+		qdf_assert_always(0);
+		/* TODO handle this if its valid case */
+	}
+
+	if (qdf_unlikely(!dp_rx_desc_check_magic(rx_desc))) {
+		dp_err("Invalid rx_desc cookie=%d", rx_buf_cookie);
+		DP_STATS_INC(soc, rx.err.rx_desc_invalid_magic, 1);
+		qdf_assert(0);
+	}
+
+	nbuf = rx_desc->nbuf;
+	QDF_NBUF_CB_RX_PKT_LEN(nbuf) =
+		HTT_RX_DATA_MSDU_INFO_MSDU_LENGTH_GET(*(msg_word + 2));
+	qdf_nbuf_set_tid_val(nbuf, HTT_RX_DATA_MSDU_INFO_TID_INFO_GET(*(msg_word + 2)));
+	QDF_NBUF_CB_RX_PEER_ID(nbuf) = peer_id;
+	QDF_NBUF_CB_RX_VDEV_ID(nbuf) = vdev_id;
+	QDF_NBUF_CB_RX_CTX_ID(nbuf) = rx_ctx_id;
+
+	dp_rx_nbuf_unmap(soc, rx_desc, rx_ctx_id);
+
+	rx_desc->unmapped = 1;
+	dp_rx_add_to_free_desc_list(&head, &tail, rx_desc);
+
+	dp_rx_buffers_replenish_simple(soc, rx_desc->pool_id,
+				       &soc->rx_refill_buf_ring[mac_id],
+				       &soc->rx_desc_buf[rx_desc->pool_id],
+				       1, &head, &tail);
+
+	if (dp_rx_buffer_pool_refill(soc, nbuf, rx_desc->pool_id))
+		/* fragment queued back to the pool no frag to handle*/
+		return;
+
+	/* Process fragment-by-fragment */
+	status = dp_rx_defrag_store_fragment_rh(soc, nbuf);
+	if (QDF_IS_STATUS_ERROR(status))
+		dp_rx_err("Unable to handle frag ret:%u", status);
+}
+
 QDF_STATUS dp_rx_desc_pool_init_rh(struct dp_soc *soc,
 				   struct rx_desc_pool *rx_desc_pool,
 				   uint32_t pool_id)

+ 40 - 0
dp/wifi3.0/rh/dp_rh_rx.h

@@ -83,6 +83,46 @@ dp_rx_peer_metadata_peer_id_get_rh(struct dp_soc *soc, uint32_t peer_metadata)
 	return metadata->peer_id;
 }
 
+/**
+ * dp_rx_data_flush() - Flush RX data after reaping from RX rings
+ *
+ * @data: reference to flush RX data
+ *
+ * Return: None
+ */
+void
+dp_rx_data_flush(void *data);
+
+/**
+ * dp_rx_data_indication_handler() - Indicate when RX data is available in rings
+ *
+ * @soc:DP soc reference
+ * @data_ind: Data indication message info
+ * @vdev_id: Vdev id
+ * @peer_id: Peer id
+ * @msdu_count: Number of MSDUs available in message
+ *
+ * Return: None
+ */
+void
+dp_rx_data_indication_handler(struct dp_soc *soc, qdf_nbuf_t data_ind,
+			      uint16_t vdev_id, uint16_t peer_id,
+			      uint16_t msdu_count);
+
+/**
+ * dp_rx_frag_indication_handler() - Indicate when RX frag data is available in ring
+ *
+ * @soc:DP soc reference
+ * @data_ind: Data indication message info
+ * @vdev_id: Vdev id
+ * @peer_id: Peer id
+ *
+ * Return: None
+ */
+void
+dp_rx_frag_indication_handler(struct dp_soc *soc, qdf_nbuf_t data_ind,
+			      uint16_t vdev_id, uint16_t peer_id);
+
 static inline bool
 dp_rx_intrabss_handle_nawds_rh(struct dp_soc *soc, struct dp_txrx_peer *ta_peer,
 			       qdf_nbuf_t nbuf_copy,