Bladeren bron

qcacmn: Multicast support for MLO

Multicast support for MLO
1. Following functions are newly added.
	dp_rx_igmp_handler()
	dp_tx_mlo_mcast_handler_be()
	dp_rx_mlo_mcast_handler_be()
	dp_mlo_get_mcast_primary_vdev()

Change-Id: If215f843369e6e2621ef302b924e524c86f0d30b
Sai Rupesh Chevuru 3 jaren geleden
bovenliggende
commit
b43e679a58

+ 5 - 1
dp/inc/cdp_txrx_cmn_struct.h

@@ -1243,6 +1243,7 @@ enum cdp_pdev_param_type {
  * @cdp_vdev_param_peer_tid_latency_enable: set peer tid latency enable flag
  * @cdp_vdev_param_mesh_tid: config tatency tid on vdev
  * @cdp_vdev_param_dscp_tid_map_id: set dscp to tid map id
+ * @cdp_vdev_param_mcast_vdev: set mcast vdev params
  *
  * @cdp_pdev_param_dbg_snf: Enable debug sniffer feature
  * @cdp_pdev_param_bpr_enable: Enable bcast probe feature
@@ -1314,6 +1315,7 @@ typedef union cdp_config_param_t {
 	uint8_t cdp_vdev_param_peer_tid_latency_enable;
 	uint8_t cdp_vdev_param_mesh_tid;
 	uint8_t cdp_vdev_param_dscp_tid_map_id;
+	bool cdp_vdev_param_mcast_vdev;
 
 	/* pdev params */
 	bool cdp_pdev_param_cptr_latcy;
@@ -1433,6 +1435,7 @@ enum cdp_pdev_bpr_param {
  * @CDP_ENABLE_PEER_TID_LATENCY: set peer tid latency enable flag
  * @CDP_SET_VAP_MESH_TID : Set latency tid in vap
  * @CDP_UPDATE_DSCP_TO_TID_MAP: Set DSCP to TID map id
+ * @CDP_SET_MCAST_VDEV : Set primary mcast vdev
  */
 enum cdp_vdev_param_type {
 	CDP_ENABLE_NAWDS,
@@ -1470,7 +1473,8 @@ enum cdp_vdev_param_type {
 #ifdef WLAN_VENDOR_SPECIFIC_BAR_UPDATE
 	CDP_SKIP_BAR_UPDATE_AP,
 #endif
-	CDP_UPDATE_DSCP_TO_TID_MAP
+	CDP_UPDATE_DSCP_TO_TID_MAP,
+	CDP_SET_MCAST_VDEV,
 };
 
 /*

+ 23 - 0
dp/wifi3.0/be/dp_be.c

@@ -1238,6 +1238,22 @@ dp_mlo_peer_find_hash_add_be(struct dp_soc *soc, struct dp_peer *peer)
 }
 #endif
 
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) && \
+	defined(WLAN_MCAST_MLO)
+static void dp_txrx_set_mlo_mcast_primary_vdev_param_be(
+					struct dp_vdev_be *be_vdev,
+					cdp_config_param_type val)
+{
+	be_vdev->mcast_primary = val.cdp_vdev_param_mcast_vdev;
+}
+#else
+static void dp_txrx_set_mlo_mcast_primary_vdev_param_be(
+					struct dp_vdev_be *be_vdev,
+					cdp_config_param_type val)
+{
+}
+#endif
+
 #ifdef DP_TX_IMPLICIT_RBM_MAPPING
 static void dp_tx_implicit_rbm_set_be(struct dp_soc *soc,
 				      uint8_t tx_ring_id,
@@ -1348,6 +1364,9 @@ QDF_STATUS dp_txrx_set_vdev_param_be(struct dp_soc *soc,
 		if (vdev->tx_encap_type == htt_cmn_pkt_type_raw)
 			dp_tx_update_bank_profile(be_soc, be_vdev);
 		break;
+	case CDP_SET_MCAST_VDEV:
+		dp_txrx_set_mlo_mcast_primary_vdev_param_be(be_vdev, val);
+		break;
 	default:
 		dp_warn("invalid param %d", param);
 		break;
@@ -1438,6 +1457,10 @@ void dp_initialize_arch_ops_be(struct dp_arch_ops *arch_ops)
 	arch_ops->txrx_set_vdev_param = dp_txrx_set_vdev_param_be;
 
 #ifdef WLAN_FEATURE_11BE_MLO
+#ifdef WLAN_MCAST_MLO
+	arch_ops->dp_tx_mcast_handler = dp_tx_mlo_mcast_handler_be;
+	arch_ops->dp_rx_mcast_handler = dp_rx_mlo_igmp_handler;
+#endif
 	arch_ops->mlo_peer_find_hash_detach =
 		dp_mlo_peer_find_hash_detach_wrapper;
 	arch_ops->mlo_peer_find_hash_attach =

+ 41 - 0
dp/wifi3.0/be/dp_be.h

@@ -256,6 +256,14 @@ struct dp_vdev_be {
 #ifdef WLAN_MLO_MULTI_CHIP
 	/* partner list used for Intra-BSS */
 	uint8_t partner_vdev_list[WLAN_MAX_MLO_CHIPS][WLAN_MAX_MLO_LINKS_PER_SOC];
+#ifdef WLAN_FEATURE_11BE_MLO
+#ifdef WLAN_MCAST_MLO
+	/* DP MLO seq number */
+	uint16_t seq_num;
+	/* MLO Mcast primary vdev */
+	bool mcast_primary;
+#endif
+#endif
 #endif
 };
 
@@ -320,6 +328,39 @@ dp_mlo_get_peer_hash_obj(struct dp_soc *soc)
 }
 
 void  dp_clr_mlo_ptnr_list(struct dp_soc *soc, struct dp_vdev *vdev);
+
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MCAST_MLO)
+typedef void dp_ptnr_vdev_iter_func(struct dp_vdev_be *be_vdev,
+				    struct dp_vdev *ptnr_vdev,
+				    void *arg);
+/*
+ * dp_mcast_mlo_iter_ptnr_vdev - API to iterate through ptnr vdev list
+ * @be_soc: dp_soc_be pointer
+ * @be_vdev: dp_vdev_be pointer
+ * @func        : function to be called for each peer
+ * @arg         : argument need to be passed to func
+ * @mod_id: module id
+ *
+ * Return: None
+ */
+void dp_mcast_mlo_iter_ptnr_vdev(struct dp_soc_be *be_soc,
+				 struct dp_vdev_be *be_vdev,
+				 dp_ptnr_vdev_iter_func func,
+				 void *arg,
+				 enum dp_mod_id mod_id);
+/*
+ * dp_mlo_get_mcast_primary_vdev- get ref to mcast primary vdev
+ * @be_soc: dp_soc_be pointer
+ * @be_vdev: dp_vdev_be pointer
+ * @mod_id: module id
+ *
+ * Return: mcast primary DP VDEV handle on success, NULL on failure
+ */
+struct dp_vdev *dp_mlo_get_mcast_primary_vdev(struct dp_soc_be *be_soc,
+					      struct dp_vdev_be *be_vdev,
+					      enum dp_mod_id mod_id);
+#endif
+
 #else
 typedef struct dp_soc_be *dp_mld_peer_hash_obj_t;
 

+ 66 - 0
dp/wifi3.0/be/dp_be_rx.c

@@ -1072,6 +1072,72 @@ struct dp_rx_desc *dp_rx_desc_cookie_2_va_be(struct dp_soc *soc,
 	return (struct dp_rx_desc *)dp_cc_desc_find(soc, cookie);
 }
 
+#if defined(WLAN_FEATURE_11BE_MLO)
+#if defined(WLAN_MLO_MULTI_CHIP) && defined(WLAN_MCAST_MLO)
+static inline void dp_rx_dummy_src_mac(qdf_nbuf_t nbuf)
+{
+	qdf_ether_header_t *eh =
+			(qdf_ether_header_t *)qdf_nbuf_data(nbuf);
+
+	eh->ether_shost[0] = 0x4d;	/* M */
+	eh->ether_shost[1] = 0x4c;	/* L */
+	eh->ether_shost[2] = 0x4d;	/* M */
+	eh->ether_shost[3] = 0x43;	/* C */
+	eh->ether_shost[4] = 0x41;	/* A */
+	eh->ether_shost[5] = 0x53;	/* S */
+}
+
+bool dp_rx_mlo_igmp_handler(struct dp_soc *soc,
+			    struct dp_vdev *vdev,
+			    struct dp_peer *peer,
+			    qdf_nbuf_t nbuf)
+{
+	struct dp_vdev *mcast_primary_vdev = NULL;
+	struct dp_vdev_be *be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
+	struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
+
+	if (!(qdf_nbuf_is_ipv4_igmp_pkt(buf) ||
+	      qdf_nbuf_is_ipv6_igmp_pkt(buf)))
+		return false;
+
+	if (vdev->mcast_enhancement_en || be_vdev->mcast_primary)
+		goto send_pkt;
+
+	mcast_primary_vdev = dp_mlo_get_mcast_primary_vdev(be_soc, be_vdev,
+							   DP_MOD_ID_RX);
+	if (!mcast_primary_vdev) {
+		dp_rx_debug("Non mlo vdev");
+		goto send_pkt;
+	}
+	dp_rx_dummy_src_mac(nbuf);
+	dp_rx_deliver_to_stack(mcast_primary_vdev->pdev->soc,
+			       mcast_primary_vdev,
+			       peer,
+			       nbuf,
+			       NULL);
+	dp_vdev_unref_delete(mcast_primary_vdev->pdev->soc,
+			     mcast_primary_vdev,
+			     DP_MOD_ID_RX);
+	return true;
+send_pkt:
+	dp_rx_deliver_to_stack(be_vdev->vdev.pdev->soc,
+			       &be_vdev->vdev,
+			       peer,
+			       nbuf,
+			       NULL);
+	return true;
+}
+#else
+bool dp_rx_mlo_igmp_handler(struct dp_soc *soc,
+			    struct dp_vdev *vdev,
+			    struct dp_peer *peer,
+			    qdf_nbuf_t nbuf)
+{
+	return false;
+}
+#endif
+#endif
+
 #ifdef WLAN_FEATURE_NEAR_FULL_IRQ
 uint32_t dp_rx_nf_process(struct dp_intr *int_ctx,
 			  hal_ring_handle_t hal_ring_hdl,

+ 19 - 0
dp/wifi3.0/be/dp_be_rx.h

@@ -209,4 +209,23 @@ dp_rx_replensih_soc_get(struct dp_soc *soc, uint8_t reo_ring_num)
 	return soc;
 }
 #endif
+
+#ifdef WLAN_FEATURE_11BE_MLO
+#ifdef WLAN_MCAST_MLO
+/**
+ * dp_rx_mlo_igmp_handler() - Rx handler for Mcast packets
+ * @soc: Handle to DP Soc structure
+ * @vdev: DP vdev handle
+ * @peer: DP peer handle
+ * @nbuf: nbuf to be enqueued
+ *
+ * Return: true when packet sent to stack, false failure
+ */
+bool dp_rx_mlo_igmp_handler(struct dp_soc *soc,
+			    struct dp_vdev *vdev,
+			    struct dp_peer *peer,
+			    qdf_nbuf_t nbuf);
+#endif
+#endif
+
 #endif

+ 96 - 1
dp/wifi3.0/be/dp_be_tx.c

@@ -38,6 +38,14 @@
 #define DP_TX_BANK_LOCK_RELEASE(lock) qdf_spin_unlock_bh(lock)
 #endif
 
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP)
+#ifdef WLAN_MCAST_MLO
+/* MLO peer id for reinject*/
+#define DP_MLO_MCAST_REINJECT_PEER_ID 0XFFFD
+#define MAX_GSN_NUM 0x0FFF
+#endif
+#endif
+
 extern uint8_t sec_type_map[MAX_CDP_SEC_TYPE];
 
 #ifdef DP_USE_REDUCED_PEER_ID_FIELD_WIDTH
@@ -156,6 +164,93 @@ static inline uint8_t dp_tx_get_rbm_id_be(struct dp_soc *soc,
 }
 #endif
 
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) && \
+	defined(WLAN_MCAST_MLO)
+void
+dp_tx_mlo_mcast_pkt_send(struct dp_vdev_be *be_vdev,
+			 struct dp_vdev *ptnr_vdev,
+			 void *arg)
+{
+	qdf_nbuf_t  nbuf = (qdf_nbuf_t)arg;
+	qdf_nbuf_t  nbuf_clone;
+	struct dp_vdev_be *be_ptnr_vdev = NULL;
+	struct dp_tx_msdu_info_s msdu_info;
+
+	be_ptnr_vdev = dp_get_be_vdev_from_dp_vdev(ptnr_vdev);
+	if (be_vdev != be_ptnr_vdev) {
+		nbuf_clone = qdf_nbuf_clone(nbuf);
+		if (qdf_unlikely(!nbuf_clone)) {
+			dp_tx_debug("nbuf clone failed");
+			return;
+		}
+	} else {
+		nbuf_clone = nbuf;
+	}
+
+	qdf_mem_zero(&msdu_info, sizeof(msdu_info));
+	dp_tx_get_queue(ptnr_vdev, nbuf_clone, &msdu_info.tx_queue);
+	msdu_info.gsn = be_vdev->seq_num;
+	be_ptnr_vdev->seq_num = be_vdev->seq_num;
+
+	nbuf_clone = dp_tx_send_msdu_single(
+					ptnr_vdev,
+					nbuf_clone,
+					&msdu_info,
+					DP_MLO_MCAST_REINJECT_PEER_ID,
+					NULL);
+	if (qdf_unlikely(nbuf_clone)) {
+		dp_info("pkt send failed");
+		qdf_nbuf_free(nbuf_clone);
+		return;
+	}
+}
+
+static inline void
+dp_tx_vdev_id_set_hal_tx_desc(uint32_t *hal_tx_desc_cached,
+			      struct dp_vdev *vdev,
+			      struct dp_tx_msdu_info_s *msdu_info)
+{
+	hal_tx_desc_set_vdev_id(hal_tx_desc_cached, msdu_info->vdev_id);
+}
+
+void dp_tx_mlo_mcast_handler_be(struct dp_soc *soc,
+				struct dp_vdev *vdev,
+				qdf_nbuf_t nbuf)
+{
+	struct dp_vdev_be *be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
+	struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
+
+	/* send frame on partner vdevs */
+	dp_mcast_mlo_iter_ptnr_vdev(be_soc, be_vdev,
+				    dp_tx_mlo_mcast_pkt_send,
+				    nbuf, DP_MOD_ID_TX);
+
+	/* send frame on mcast primary vdev */
+	dp_tx_mlo_mcast_pkt_send(be_vdev, vdev, nbuf);
+
+	if (qdf_unlikely(be_vdev->seq_num > MAX_GSN_NUM))
+		be_vdev->seq_num = 0;
+	else
+		be_vdev->seq_num++;
+}
+#else
+static inline void
+dp_tx_vdev_id_set_hal_tx_desc(uint32_t *hal_tx_desc_cached,
+			      struct dp_vdev *vdev,
+			      struct dp_tx_msdu_info_s *msdu_info)
+{
+	hal_tx_desc_set_vdev_id(hal_tx_desc_cached, vdev->vdev_id);
+}
+#endif
+#if defined(WLAN_FEATURE_11BE_MLO) && !defined(WLAN_MLO_MULTI_CHIP) && \
+	!defined(WLAN_MCAST_MLO)
+void dp_tx_mlo_mcast_handler_be(struct dp_soc *soc,
+				struct dp_vdev *vdev,
+				qdf_nbuf_t nbuf)
+{
+}
+#endif
+
 QDF_STATUS
 dp_tx_hw_enqueue_be(struct dp_soc *soc, struct dp_vdev *vdev,
 		    struct dp_tx_desc_s *tx_desc, uint16_t fw_metadata,
@@ -229,7 +324,7 @@ dp_tx_hw_enqueue_be(struct dp_soc *soc, struct dp_vdev *vdev,
 
 	hal_tx_desc_set_bank_id(hal_tx_desc_cached, be_vdev->bank_id);
 
-	hal_tx_desc_set_vdev_id(hal_tx_desc_cached, vdev->vdev_id);
+	dp_tx_vdev_id_set_hal_tx_desc(hal_tx_desc_cached, vdev, msdu_info);
 
 	if (tid != HTT_TX_EXT_TID_INVALID)
 		hal_tx_desc_set_hlos_tid(hal_tx_desc_cached, tid);

+ 30 - 0
dp/wifi3.0/be/dp_be_tx.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -139,6 +140,35 @@ void dp_tx_desc_pool_deinit_be(struct dp_soc *soc,
 			       struct dp_tx_desc_pool_s *tx_desc_pool,
 			       uint8_t pool_id);
 
+#ifdef WLAN_FEATURE_11BE_MLO
+#ifdef WLAN_MCAST_MLO
+/**
+ * dp_tx_mlo_mcast_handler_be() - Tx handler for Mcast packets
+ * @soc: Handle to DP Soc structure
+ * @vdev: DP vdev handle
+ * @nbuf: nbuf to be enqueued
+ *
+ * Return: None
+ */
+void dp_tx_mlo_mcast_handler_be(struct dp_soc *soc,
+				struct dp_vdev *vdev,
+				qdf_nbuf_t nbuf);
+#ifdef WLAN_MLO_MULTI_CHIP
+/**
+ * dp_tx_mlo_mcast_pkt_send() - handler to send MLO Mcast packets
+ * @be_vdev: Handle to DP be_vdev structure
+ * @ptnr_vdev: DP ptnr_vdev handle
+ * @nbuf: nbuf to be enqueued
+ *
+ * Return: None
+ */
+void dp_tx_mlo_mcast_pkt_send(struct dp_vdev_be *be_vdev,
+			      struct dp_vdev *ptnr_vdev,
+			      void *arg);
+#endif
+#endif
+#endif
+
 #ifdef WLAN_FEATURE_NEAR_FULL_IRQ
 /**
  * dp_tx_comp_nf_handler() - Tx completion ring Near full scenario handler

+ 74 - 0
dp/wifi3.0/be/mlo/dp_mlo.c

@@ -472,3 +472,77 @@ dp_rx_replensih_soc_get(struct dp_soc *soc, uint8_t reo_ring_num)
 
 	return soc;
 }
+
+#ifdef WLAN_MCAST_MLO
+void dp_mcast_mlo_iter_ptnr_vdev(struct dp_soc_be *be_soc,
+				 struct dp_vdev_be *be_vdev,
+				 dp_ptnr_vdev_iter_func func,
+				 void *arg,
+				 enum dp_mod_id mod_id)
+{
+	int i = 0;
+	int j = 0;
+	struct dp_mlo_ctxt *dp_mlo = be_soc->ml_ctxt;
+
+	for (i = 0; i < WLAN_MAX_MLO_CHIPS ; i++) {
+		struct dp_soc *ptnr_soc =
+				dp_mlo_get_soc_ref_by_chip_id(dp_mlo, i);
+
+		if (!ptnr_soc)
+			continue;
+		for (j = 0 ; j < WLAN_MAX_MLO_LINKS_PER_SOC ; j++) {
+			struct dp_vdev *ptnr_vdev;
+
+			ptnr_vdev = dp_vdev_get_ref_by_id(
+					ptnr_soc,
+					be_vdev->partner_vdev_list[i][j],
+					mod_id);
+			if (!ptnr_vdev)
+				continue;
+			(*func)(be_vdev, ptnr_vdev, arg);
+			dp_vdev_unref_delete(ptnr_vdev->pdev->soc,
+					     ptnr_vdev,
+					     mod_id);
+		}
+	}
+}
+
+qdf_export_symbol(dp_mcast_mlo_iter_ptnr_vdev);
+
+struct dp_vdev *dp_mlo_get_mcast_primary_vdev(struct dp_soc_be *be_soc,
+					      struct dp_vdev_be *be_vdev,
+					      enum dp_mod_id mod_id)
+{
+	int i = 0;
+	int j = 0;
+	struct dp_mlo_ctxt *dp_mlo = be_soc->ml_ctxt;
+
+	for (i = 0; i < WLAN_MAX_MLO_CHIPS ; i++) {
+		struct dp_soc *ptnr_soc =
+				dp_mlo_get_soc_ref_by_chip_id(dp_mlo, i);
+
+		if (!ptnr_soc)
+			continue;
+		for (j = 0 ; j < WLAN_MAX_MLO_LINKS_PER_SOC ; j++) {
+			struct dp_vdev *ptnr_vdev = NULL;
+			struct dp_vdev_be *be_ptnr_vdev = NULL;
+
+			ptnr_vdev = dp_vdev_get_ref_by_id(
+					ptnr_soc,
+					be_vdev->partner_vdev_list[i][j],
+					mod_id);
+			if (!ptnr_vdev)
+				continue;
+			be_ptnr_vdev = dp_get_be_vdev_from_dp_vdev(ptnr_vdev);
+			if (be_ptnr_vdev->mcast_primary)
+				return ptnr_vdev;
+			dp_vdev_unref_delete(be_ptnr_vdev->vdev.pdev->soc,
+					     &be_ptnr_vdev->vdev,
+					     mod_id);
+		}
+	}
+	return NULL;
+}
+
+qdf_export_symbol(dp_mlo_get_mcast_primary_vdev);
+#endif

+ 84 - 12
dp/wifi3.0/dp_htt.c

@@ -327,25 +327,77 @@ dp_htt_h2t_send_complete(void *context, HTC_PACKET *htc_pkt)
 
 #endif /* ENABLE_CE4_COMP_DISABLE_HTT_HTC_MISC_LIST */
 
+#ifdef WLAN_MCAST_MLO
 /*
- * htt_h2t_ver_req_msg() - Send HTT version request message to target
+ * dp_htt_h2t_add_tcl_metadata_verg() - Add tcl_metadata verion
  * @htt_soc:	HTT SOC handle
+ * @msg:	Pointer to nbuf
  *
  * Return: 0 on success; error code on failure
  */
-static int htt_h2t_ver_req_msg(struct htt_soc *soc)
+static int dp_htt_h2t_add_tcl_metadata_ver(struct htt_soc *soc, qdf_nbuf_t *msg)
 {
-	struct dp_htt_htc_pkt *pkt;
-	qdf_nbuf_t msg;
 	uint32_t *msg_word;
-	QDF_STATUS status;
 
-	msg = qdf_nbuf_alloc(
+	*msg = qdf_nbuf_alloc(
+		soc->osdev,
+		HTT_MSG_BUF_SIZE(HTT_VER_REQ_BYTES + HTT_TCL_METADATA_VER_SZ),
+		/* reserve room for the HTC header */
+		HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, TRUE);
+	if (!*msg)
+		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.
+	 */
+	if (!qdf_nbuf_put_tail(*msg,
+			       HTT_VER_REQ_BYTES + HTT_TCL_METADATA_VER_SZ)) {
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Failed to expand head for HTT_H2T_MSG_TYPE_VERSION_REQ msg",
+			  __func__);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	/* fill in the message contents */
+	msg_word = (u_int32_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);
+
+	*msg_word = 0;
+	HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_VERSION_REQ);
+
+	/* word 1 */
+	msg_word++;
+	*msg_word = 0;
+	HTT_OPTION_TLV_TAG_SET(*msg_word, HTT_OPTION_TLV_TAG_TCL_METADATA_VER);
+	HTT_OPTION_TLV_LENGTH_SET(*msg_word, HTT_TCL_METADATA_VER_SZ);
+	HTT_OPTION_TLV_TCL_METADATA_VER_SET(*msg_word,
+					    HTT_OPTION_TLV_TCL_METADATA_V2);
+
+	return QDF_STATUS_SUCCESS;
+}
+#else
+/*
+ * dp_htt_h2t_add_tcl_metadata_verg() - Add tcl_metadata verion
+ * @htt_soc:	HTT SOC handle
+ * @msg:	Pointer to nbuf
+ *
+ * Return: 0 on success; error code on failure
+ */
+static int dp_htt_h2t_add_tcl_metadata_ver(struct htt_soc *soc, qdf_nbuf_t *msg)
+{
+	uint32_t *msg_word;
+
+	*msg = qdf_nbuf_alloc(
 		soc->osdev,
 		HTT_MSG_BUF_SIZE(HTT_VER_REQ_BYTES),
 		/* reserve room for the HTC header */
 		HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, TRUE);
-	if (!msg)
+	if (!*msg)
 		return QDF_STATUS_E_NOMEM;
 
 	/*
@@ -354,22 +406,42 @@ static int htt_h2t_ver_req_msg(struct htt_soc *soc)
 	 * separately during the below call to qdf_nbuf_push_head.
 	 * The contribution from the HTC header is added separately inside HTC.
 	 */
-	if (qdf_nbuf_put_tail(msg, HTT_VER_REQ_BYTES) == NULL) {
+	if (!qdf_nbuf_put_tail(*msg, HTT_VER_REQ_BYTES)) {
 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-			"%s: Failed to expand head for HTT_H2T_MSG_TYPE_VERSION_REQ msg",
-			__func__);
+			  "%s: Failed to expand head for HTT_H2T_MSG_TYPE_VERSION_REQ msg",
+			  __func__);
 		return QDF_STATUS_E_FAILURE;
 	}
 
 	/* fill in the message contents */
-	msg_word = (u_int32_t *) qdf_nbuf_data(msg);
+	msg_word = (u_int32_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);
+	qdf_nbuf_push_head(*msg, HTC_HDR_ALIGNMENT_PADDING);
 
 	*msg_word = 0;
 	HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_VERSION_REQ);
 
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
+/*
+ * htt_h2t_ver_req_msg() - Send HTT version request message to target
+ * @htt_soc:	HTT SOC handle
+ *
+ * Return: 0 on success; error code on failure
+ */
+static int htt_h2t_ver_req_msg(struct htt_soc *soc)
+{
+	struct dp_htt_htc_pkt *pkt;
+	qdf_nbuf_t msg = NULL;
+	QDF_STATUS status;
+
+	status = dp_htt_h2t_add_tcl_metadata_ver(soc, &msg);
+	if (status != QDF_STATUS_SUCCESS)
+		return status;
+
 	pkt = htt_htc_pkt_alloc(soc);
 	if (!pkt) {
 		qdf_nbuf_free(msg);

+ 10 - 2
dp/wifi3.0/dp_main.c

@@ -111,6 +111,14 @@ cdp_dump_flow_pool_info(struct cdp_soc_t *soc)
 #define WLAN_SYSFS_STAT_REQ_WAIT_MS 3000
 #endif
 
+#ifdef WLAN_MCAST_MLO
+#define DP_TX_TCL_METADATA_PDEV_ID_SET(_var, _val) \
+		HTT_TX_TCL_METADATA_V2_PDEV_ID_SET(_var, _val)
+#else
+#define DP_TX_TCL_METADATA_PDEV_ID_SET(_var, _val) \
+		HTT_TX_TCL_METADATA_PDEV_ID_SET(_var, _val)
+#endif
+
 QDF_COMPILE_TIME_ASSERT(max_rx_rings_check,
 			MAX_REO_DEST_RINGS == CDP_MAX_RX_RINGS);
 
@@ -11136,8 +11144,8 @@ dp_soc_handle_pdev_mode_change
 
 	qdf_spin_lock_bh(&pdev->vdev_list_lock);
 	TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
-		HTT_TX_TCL_METADATA_PDEV_ID_SET(vdev->htt_tcl_metadata,
-						hw_pdev_id);
+		DP_TX_TCL_METADATA_PDEV_ID_SET(vdev->htt_tcl_metadata,
+					       hw_pdev_id);
 		vdev->lmac_id = pdev->lmac_id;
 	}
 	qdf_spin_unlock_bh(&pdev->vdev_list_lock);

+ 26 - 0
dp/wifi3.0/dp_rx_err.c

@@ -1764,6 +1764,29 @@ dp_rx_deliver_to_osif_stack(struct dp_soc *soc,
 #endif
 
 #ifdef WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) && \
+	defined(WLAN_MCAST_MLO)
+static bool dp_rx_igmp_handler(struct dp_soc *soc,
+			       struct dp_vdev *vdev,
+			       struct dp_peer *peer,
+			       qdf_nbuf_t nbuf)
+{
+	if (soc->arch_ops.dp_rx_mcast_handler) {
+		if (soc->arch_ops.dp_rx_mcast_handler(soc, vdev, peer, nbuf))
+			return true;
+	}
+	return false;
+}
+#else
+static bool dp_rx_igmp_handler(struct dp_soc *soc,
+			       struct dp_vdev *vdev,
+			       struct dp_peer *peer,
+			       qdf_nbuf_t nbuf)
+{
+	return false;
+}
+#endif
+
 /**
  * dp_rx_err_route_hdl() - Function to send EAPOL frames to stack
  *                            Free any other packet which comes in
@@ -1836,6 +1859,9 @@ dp_rx_err_route_hdl(struct dp_soc *soc, qdf_nbuf_t nbuf,
 		qdf_nbuf_pull_head(nbuf, (msdu_metadata.l3_hdr_pad +
 				   soc->rx_pkt_tlv_size));
 
+	if (dp_rx_igmp_handler(soc, vdev, peer, nbuf))
+		return;
+
 	dp_vdev_peer_stats_update_protocol_cnt(vdev, nbuf, NULL, 0, 1);
 
 	/*

+ 114 - 19
dp/wifi3.0/dp_tx.c

@@ -65,6 +65,42 @@
 
 #define DP_RETRY_COUNT 7
 
+#ifdef WLAN_MCAST_MLO
+#define DP_TX_TCL_METADATA_PDEV_ID_SET(_var, _val)\
+	HTT_TX_TCL_METADATA_V2_PDEV_ID_SET(_var, _val)
+#define DP_TX_TCL_METADATA_VALID_HTT_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_V2_VALID_HTT_SET(_var, _val)
+#define DP_TX_TCL_METADATA_TYPE_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_TYPE_V2_SET(_var, _val)
+#define DP_TX_TCL_METADATA_HOST_INSPECTED_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_V2_HOST_INSPECTED_SET(_var, _val)
+#define DP_TX_TCL_METADATA_PEER_ID_SET(_var, _val) \
+	 HTT_TX_TCL_METADATA_V2_PEER_ID_SET(_var, _val)
+#define DP_TX_TCL_METADATA_VDEV_ID_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_V2_VDEV_ID_SET(_var, _val)
+#define DP_TCL_METADATA_TYPE_PEER_BASED \
+	HTT_TCL_METADATA_V2_TYPE_PEER_BASED
+#define DP_TCL_METADATA_TYPE_VDEV_BASED \
+	HTT_TCL_METADATA_V2_TYPE_VDEV_BASED
+#else
+#define DP_TX_TCL_METADATA_PDEV_ID_SET(_var, _val)\
+	HTT_TX_TCL_METADATA_PDEV_ID_SET(_var, _val)
+#define DP_TX_TCL_METADATA_VALID_HTT_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_VALID_HTT_SET(_var, _val)
+#define DP_TX_TCL_METADATA_TYPE_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_TYPE_SET(_var, _val)
+#define DP_TX_TCL_METADATA_HOST_INSPECTED_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_HOST_INSPECTED_SET(_var, _val)
+#define DP_TX_TCL_METADATA_PEER_ID_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_PEER_ID_SET(_var, _val)
+#define DP_TX_TCL_METADATA_VDEV_ID_SET(_var, _val) \
+	HTT_TX_TCL_METADATA_VDEV_ID_SET(_var, _val)
+#define DP_TCL_METADATA_TYPE_PEER_BASED \
+	HTT_TCL_METADATA_TYPE_PEER_BASED
+#define DP_TCL_METADATA_TYPE_VDEV_BASED \
+	HTT_TCL_METADATA_TYPE_VDEV_BASED
+#endif
+
 /*mapping between hal encrypt type and cdp_sec_type*/
 uint8_t sec_type_map[MAX_CDP_SEC_TYPE] = {HAL_TX_ENCRYPT_TYPE_NO_CIPHER,
 					  HAL_TX_ENCRYPT_TYPE_WEP_128,
@@ -1908,6 +1944,41 @@ int dp_tx_frame_is_drop(struct dp_vdev *vdev, uint8_t *srcmac, uint8_t *dstmac)
 	return 0;
 }
 
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) && \
+	defined(WLAN_MCAST_MLO)
+/* MLO peer id for reinject*/
+#define DP_MLO_MCAST_REINJECT_PEER_ID 0XFFFD
+/* MLO vdev id inc offset */
+#define DP_MLO_VDEV_ID_OFFSET 0x80
+
+static inline void
+dp_tx_update_mcast_param(uint16_t peer_id,
+			 uint16_t *htt_tcl_metadata,
+			 struct dp_vdev *vdev,
+			 struct dp_tx_msdu_info_s *msdu_info)
+{
+	if (peer_id == DP_MLO_MCAST_REINJECT_PEER_ID) {
+		*htt_tcl_metadata = 0;
+		DP_TX_TCL_METADATA_TYPE_SET(
+				*htt_tcl_metadata,
+				HTT_TCL_METADATA_V2_TYPE_GLOBAL_SEQ_BASED);
+		HTT_TX_TCL_METADATA_GLBL_SEQ_NO_SET(*htt_tcl_metadata,
+						    msdu_info->gsn);
+
+		msdu_info->vdev_id = vdev->vdev_id + DP_MLO_VDEV_ID_OFFSET;
+	} else {
+		msdu_info->vdev_id = vdev->vdev_id;
+	}
+}
+#else
+static inline void
+dp_tx_update_mcast_param(uint16_t peer_id,
+			 uint16_t *htt_tcl_metadata,
+			 struct dp_vdev *vdev,
+			 struct dp_tx_msdu_info_s *msdu_info)
+{
+}
+#endif
 /**
  * dp_tx_send_msdu_single() - Setup descriptor and enqueue single MSDU to TCL
  * @vdev: DP vdev handle
@@ -1950,17 +2021,17 @@ dp_tx_send_msdu_single(struct dp_vdev *vdev, qdf_nbuf_t nbuf,
 
 	if (qdf_unlikely(peer_id == DP_INVALID_PEER)) {
 		htt_tcl_metadata = vdev->htt_tcl_metadata;
-		HTT_TX_TCL_METADATA_HOST_INSPECTED_SET(htt_tcl_metadata, 1);
+		DP_TX_TCL_METADATA_HOST_INSPECTED_SET(htt_tcl_metadata, 1);
 	} else if (qdf_unlikely(peer_id != HTT_INVALID_PEER)) {
-		HTT_TX_TCL_METADATA_TYPE_SET(htt_tcl_metadata,
-				HTT_TCL_METADATA_TYPE_PEER_BASED);
-		HTT_TX_TCL_METADATA_PEER_ID_SET(htt_tcl_metadata,
-				peer_id);
+		DP_TX_TCL_METADATA_TYPE_SET(htt_tcl_metadata,
+					    DP_TCL_METADATA_TYPE_PEER_BASED);
+		DP_TX_TCL_METADATA_PEER_ID_SET(htt_tcl_metadata,
+					       peer_id);
 	} else
 		htt_tcl_metadata = vdev->htt_tcl_metadata;
 
 	if (msdu_info->exception_fw)
-		HTT_TX_TCL_METADATA_VALID_HTT_SET(htt_tcl_metadata, 1);
+		DP_TX_TCL_METADATA_VALID_HTT_SET(htt_tcl_metadata, 1);
 
 	dp_tx_desc_update_fast_comp_flag(soc, tx_desc,
 					 !pdev->enhanced_stats_en);
@@ -1979,6 +2050,7 @@ dp_tx_send_msdu_single(struct dp_vdev *vdev, qdf_nbuf_t nbuf,
 	tx_desc->dma_addr = qdf_nbuf_mapped_paddr_get(tx_desc->nbuf);
 	dp_tx_desc_history_add(soc, tx_desc->dma_addr, nbuf,
 			       tx_desc->id, DP_TX_DESC_MAP);
+	dp_tx_update_mcast_param(peer_id, &htt_tcl_metadata, vdev, msdu_info);
 	/* Enqueue the Tx MSDU descriptor to HW for transmit */
 	status = soc->arch_ops.tx_hw_enqueue(soc, vdev, tx_desc,
 					     htt_tcl_metadata,
@@ -2177,7 +2249,7 @@ qdf_nbuf_t dp_tx_send_msdu_multiple(struct dp_vdev *vdev, qdf_nbuf_t nbuf,
 
 		htt_tcl_metadata = vdev->htt_tcl_metadata;
 		if (msdu_info->exception_fw) {
-			HTT_TX_TCL_METADATA_VALID_HTT_SET(htt_tcl_metadata, 1);
+			DP_TX_TCL_METADATA_VALID_HTT_SET(htt_tcl_metadata, 1);
 		}
 
 		/*
@@ -2204,6 +2276,10 @@ qdf_nbuf_t dp_tx_send_msdu_multiple(struct dp_vdev *vdev, qdf_nbuf_t nbuf,
 			}
 		}
 
+		dp_tx_update_mcast_param(DP_INVALID_PEER,
+					 &htt_tcl_metadata,
+					 vdev,
+					 msdu_info);
 		/*
 		 * Enqueue the Tx MSDU descriptor to HW for transmit
 		 */
@@ -3226,6 +3302,7 @@ int dp_tx_proxy_arp(struct dp_vdev *vdev, qdf_nbuf_t nbuf)
  * @vdev: datapath vdev handle
  * @tx_desc: software descriptor head pointer
  * @status : Tx completion status from HTT descriptor
+ * @reinject_reason : reinject reason from HTT descriptor
  *
  * This function reinjects frames back to Target.
  * Todo - Host queue needs to be added
@@ -3236,7 +3313,8 @@ static
 void dp_tx_reinject_handler(struct dp_soc *soc,
 			    struct dp_vdev *vdev,
 			    struct dp_tx_desc_s *tx_desc,
-			    uint8_t *status)
+			    uint8_t *status,
+			    uint8_t reinject_reason)
 {
 	struct dp_peer *peer = NULL;
 	uint32_t peer_id = HTT_INVALID_PEER;
@@ -3252,15 +3330,23 @@ void dp_tx_reinject_handler(struct dp_soc *soc,
 
 	qdf_assert(vdev);
 
-	qdf_mem_zero(&msdu_info, sizeof(msdu_info));
-
-	dp_tx_get_queue(vdev, nbuf, &msdu_info.tx_queue);
-
 	dp_tx_debug("Tx reinject path");
 
 	DP_STATS_INC_PKT(vdev, tx_i.reinject_pkts, 1,
 			qdf_nbuf_len(tx_desc->nbuf));
 
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP)
+#ifdef WLAN_MCAST_MLO
+	if (reinject_reason == HTT_TX_FW2WBM_REINJECT_REASON_MLO_MCAST) {
+		if (soc->arch_ops.dp_tx_mcast_handler)
+			soc->arch_ops.dp_tx_mcast_handler(soc, vdev, nbuf);
+
+		dp_tx_desc_release(tx_desc, tx_desc->pool_id);
+		return;
+	}
+#endif
+#endif
+
 #ifdef WDS_VENDOR_EXTENSION
 	if (qdf_unlikely(vdev->tx_encap_type != htt_cmn_pkt_type_raw)) {
 		is_mcast = (IS_MULTICAST(wh->i_addr1)) ? 1 : 0;
@@ -3318,6 +3404,9 @@ void dp_tx_reinject_handler(struct dp_soc *soc,
 					dp_tx_debug("nbuf copy failed");
 					break;
 				}
+				qdf_mem_zero(&msdu_info, sizeof(msdu_info));
+				dp_tx_get_queue(vdev, nbuf,
+						&msdu_info.tx_queue);
 
 				nbuf_copy = dp_tx_send_msdu_single(vdev,
 						nbuf_copy,
@@ -4498,7 +4587,13 @@ void dp_tx_process_htt_completion(struct dp_soc *soc,
 	}
 	case HTT_TX_FW2WBM_TX_STATUS_REINJECT:
 	{
-		dp_tx_reinject_handler(soc, vdev, tx_desc, status);
+		uint8_t reinject_reason;
+
+		reinject_reason =
+			HTT_TX_WBM_COMPLETION_V2_REINJECT_REASON_GET(
+								htt_desc[0]);
+		dp_tx_reinject_handler(soc, vdev, tx_desc,
+				       status, reinject_reason);
 		break;
 	}
 	case HTT_TX_FW2WBM_TX_STATUS_INSPECT:
@@ -4843,21 +4938,21 @@ QDF_STATUS dp_tx_vdev_attach(struct dp_vdev *vdev)
 	/*
 	 * Fill HTT TCL Metadata with Vdev ID and MAC ID
 	 */
-	HTT_TX_TCL_METADATA_TYPE_SET(vdev->htt_tcl_metadata,
-				     HTT_TCL_METADATA_TYPE_VDEV_BASED);
+	DP_TX_TCL_METADATA_TYPE_SET(vdev->htt_tcl_metadata,
+				    DP_TCL_METADATA_TYPE_VDEV_BASED);
 
-	HTT_TX_TCL_METADATA_VDEV_ID_SET(vdev->htt_tcl_metadata,
-					vdev->vdev_id);
+	DP_TX_TCL_METADATA_VDEV_ID_SET(vdev->htt_tcl_metadata,
+				       vdev->vdev_id);
 
 	pdev_id =
 		dp_get_target_pdev_id_for_host_pdev_id(vdev->pdev->soc,
 						       vdev->pdev->pdev_id);
-	HTT_TX_TCL_METADATA_PDEV_ID_SET(vdev->htt_tcl_metadata, pdev_id);
+	DP_TX_TCL_METADATA_PDEV_ID_SET(vdev->htt_tcl_metadata, pdev_id);
 
 	/*
 	 * Set HTT Extension Valid bit to 0 by default
 	 */
-	HTT_TX_TCL_METADATA_VALID_HTT_SET(vdev->htt_tcl_metadata, 0);
+	DP_TX_TCL_METADATA_VALID_HTT_SET(vdev->htt_tcl_metadata, 0);
 
 	dp_tx_vdev_update_search_flags(vdev);
 

+ 8 - 0
dp/wifi3.0/dp_tx.h

@@ -179,6 +179,8 @@ struct dp_tx_queue {
  * @exception_fw: Duplicate frame to be sent to firmware
  * @ppdu_cookie: 16-bit ppdu_cookie that has to be replayed back in completions
  * @ix_tx_sniffer: Indicates if the packet has to be sniffed
+ * @gsn: global sequence for reinjected mcast packets
+ * @vdev_id : vdev_id for reinjected mcast packets
  *
  * This structure holds the complete MSDU information needed to program the
  * Hardware TCL and MSDU extension descriptors for different frame types
@@ -197,6 +199,12 @@ struct dp_tx_msdu_info_s {
 	} u;
 	uint32_t meta_data[DP_TX_MSDU_INFO_META_DATA_DWORDS];
 	uint16_t ppdu_cookie;
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP)
+#ifdef WLAN_MCAST_MLO
+	uint16_t gsn;
+	uint8_t vdev_id;
+#endif
+#endif
 };
 
 #ifndef QCA_HOST_MODE_WIFI_DISABLED

+ 6 - 0
dp/wifi3.0/dp_types.h

@@ -1713,6 +1713,12 @@ struct dp_arch_ops {
 
 	/* MLO ops */
 #ifdef WLAN_FEATURE_11BE_MLO
+#ifdef WLAN_MCAST_MLO
+	void (*dp_tx_mcast_handler)(struct dp_soc *soc, struct dp_vdev *vdev,
+				    qdf_nbuf_t nbuf);
+	bool (*dp_rx_mcast_handler)(struct dp_soc *soc, struct dp_vdev *vdev,
+				    struct dp_peer *peer, qdf_nbuf_t nbuf);
+#endif
 	void (*mlo_peer_find_hash_detach)(struct dp_soc *soc);
 	QDF_STATUS (*mlo_peer_find_hash_attach)(struct dp_soc *soc);
 	void (*mlo_peer_find_hash_add)(struct dp_soc *soc,

+ 30 - 0
qdf/inc/qdf_nbuf.h

@@ -75,6 +75,7 @@
 #define QDF_NBUF_TRAC_IPV6_OFFSET		14
 #define QDF_NBUF_TRAC_IPV6_HEADER_SIZE   40
 #define QDF_NBUF_TRAC_ICMP_TYPE         1
+#define QDF_NBUF_TRAC_IGMP_TYPE         2
 #define QDF_NBUF_TRAC_TCP_TYPE          6
 #define QDF_NBUF_TRAC_TCP_FLAGS_OFFSET       (47 - 34)
 #define QDF_NBUF_TRAC_TCP_ACK_OFFSET         (42 - 34)
@@ -84,6 +85,7 @@
 #define QDF_NBUF_TRAC_TCP_DPORT_OFFSET       (36 - 34)
 #define QDF_NBUF_TRAC_UDP_TYPE          17
 #define QDF_NBUF_TRAC_ICMPV6_TYPE       0x3a
+#define QDF_NBUF_TRAC_HOPOPTS_TYPE      0
 #define QDF_NBUF_TRAC_DHCP6_SRV_PORT		547
 #define QDF_NBUF_TRAC_DHCP6_CLI_PORT		546
 #define QDF_NBUF_TRAC_MDNS_SRC_N_DST_PORT	5353
@@ -2886,6 +2888,34 @@ bool qdf_nbuf_is_ipv4_wapi_pkt(qdf_nbuf_t buf)
 	return __qdf_nbuf_is_ipv4_wapi_pkt(buf);
 }
 
+/**
+ * qdf_nbuf_is_ipv4_igmp_pkt() - check if packet is a igmp packet or not
+ * @buf:  buffer
+ *
+ * This api is for ipv4 packet.
+ *
+ * Return: true if packet is igmp packet
+ */
+static inline
+bool qdf_nbuf_is_ipv4_igmp_pkt(qdf_nbuf_t buf)
+{
+	return __qdf_nbuf_data_is_ipv4_igmp_pkt(qdf_nbuf_data(buf));
+}
+
+/**
+ * qdf_nbuf_is_ipv6_igmp_pkt() - check if packet is a igmp packet or not
+ * @buf:  buffer
+ *
+ * This api is for ipv6 packet.
+ *
+ * Return: true if packet is igmp packet
+ */
+static inline
+bool qdf_nbuf_is_ipv6_igmp_pkt(qdf_nbuf_t buf)
+{
+	return __qdf_nbuf_data_is_ipv6_igmp_pkt(qdf_nbuf_data(buf));
+}
+
 /**
  * qdf_nbuf_is_ipv4_tdls_pkt() - check if packet is a tdls packet or not
  * @buf:  buffer

+ 2 - 0
qdf/linux/src/i_qdf_nbuf.h

@@ -861,6 +861,8 @@ bool __qdf_nbuf_data_is_ipv4_dhcp_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv6_dhcp_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv6_mdns_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv4_eapol_pkt(uint8_t *data);
+bool __qdf_nbuf_data_is_ipv4_igmp_pkt(uint8_t *data);
+bool __qdf_nbuf_data_is_ipv6_igmp_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv4_arp_pkt(uint8_t *data);
 bool __qdf_nbuf_is_bcast_pkt(__qdf_nbuf_t nbuf);
 bool __qdf_nbuf_data_is_arp_req(uint8_t *data);

+ 56 - 0
qdf/linux/src/qdf_nbuf.c

@@ -1614,6 +1614,62 @@ bool __qdf_nbuf_is_ipv4_wapi_pkt(struct sk_buff *skb)
 }
 qdf_export_symbol(__qdf_nbuf_is_ipv4_wapi_pkt);
 
+/**
+ * __qdf_nbuf_data_is_ipv4_igmp_pkt() - check if skb data is a igmp packet
+ * @data: Pointer to network data buffer
+ *
+ * This api is for ipv4 packet.
+ *
+ * Return: true if packet is igmp packet
+ *	   false otherwise.
+ */
+bool __qdf_nbuf_data_is_ipv4_igmp_pkt(uint8_t *data)
+{
+	if (__qdf_nbuf_data_is_ipv4_pkt(data)) {
+		uint8_t pkt_type;
+
+		pkt_type = (uint8_t)(*(uint8_t *)(data +
+				QDF_NBUF_TRAC_IPV4_PROTO_TYPE_OFFSET));
+
+		if (pkt_type == QDF_NBUF_TRAC_IGMP_TYPE)
+			return true;
+	}
+	return false;
+}
+
+qdf_export_symbol(__qdf_nbuf_data_is_ipv4_igmp_pkt);
+
+/**
+ * __qdf_nbuf_data_is_ipv6_igmp_pkt() - check if skb data is a igmp packet
+ * @data: Pointer to network data buffer
+ *
+ * This api is for ipv6 packet.
+ *
+ * Return: true if packet is igmp packet
+ *	   false otherwise.
+ */
+bool __qdf_nbuf_data_is_ipv6_igmp_pkt(uint8_t *data)
+{
+	if (__qdf_nbuf_data_is_ipv6_pkt(data)) {
+		uint8_t pkt_type;
+		uint8_t next_hdr;
+
+		pkt_type = (uint8_t)(*(uint8_t *)(data +
+				QDF_NBUF_TRAC_IPV6_PROTO_TYPE_OFFSET));
+		next_hdr = (uint8_t)(*(uint8_t *)(data +
+				QDF_NBUF_TRAC_IPV6_HEADER_SIZE));
+
+		if (pkt_type == QDF_NBUF_TRAC_ICMPV6_TYPE)
+			return true;
+		if ((pkt_type == QDF_NBUF_TRAC_HOPOPTS_TYPE) &&
+		    (next_hdr == QDF_NBUF_TRAC_HOPOPTS_TYPE))
+			return true;
+	}
+	return false;
+}
+
+qdf_export_symbol(__qdf_nbuf_data_is_ipv6_igmp_pkt);
+
 /**
  * __qdf_nbuf_is_ipv4_tdls_pkt() - check if skb data is a tdls packet
  * @skb: Pointer to network buffer