ソースを参照

Merge "qcacmn: Handling of multicast packets in MLO multi passphrase"

Linux Build Service Account 3 年 前
コミット
fd62d7de42

+ 218 - 0
dp/wifi3.0/be/dp_be_tx.c

@@ -47,6 +47,20 @@
 /* MLO peer id for reinject*/
 #define DP_MLO_MCAST_REINJECT_PEER_ID 0XFFFD
 #define MAX_GSN_NUM 0x0FFF
+
+#ifdef QCA_MULTIPASS_SUPPORT
+#define INVALID_VLAN_ID         0xFFFF
+#define MULTIPASS_WITH_VLAN_ID 0xFFFE
+/**
+ * struct dp_mlo_mpass_buf - Multipass buffer
+ * @vlan_id: vlan_id of frame
+ * @nbuf: pointer to skb buf
+ */
+struct dp_mlo_mpass_buf {
+	uint16_t vlan_id;
+	qdf_nbuf_t  nbuf;
+};
+#endif
 #endif
 #endif
 
@@ -419,6 +433,207 @@ dp_tx_set_min_rates_for_critical_frames(struct dp_soc *soc,
 
 #if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) && \
 	defined(WLAN_MCAST_MLO)
+#ifdef QCA_MULTIPASS_SUPPORT
+/**
+ * dp_tx_mlo_mcast_multipass_lookup() - lookup vlan_id in mpass peer list
+ * @be_vdev: Handle to DP be_vdev structure
+ * @ptnr_vdev: DP ptnr_vdev handle
+ * @arg: pointer to dp_mlo_mpass_ buf
+ *
+ * Return: None
+ */
+static void
+dp_tx_mlo_mcast_multipass_lookup(struct dp_vdev_be *be_vdev,
+				 struct dp_vdev *ptnr_vdev,
+				 void *arg)
+{
+	struct dp_mlo_mpass_buf *ptr = (struct dp_mlo_mpass_buf *)arg;
+	struct dp_txrx_peer *txrx_peer = NULL;
+	struct vlan_ethhdr *veh = NULL;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *)qdf_nbuf_data(ptr->nbuf);
+	uint16_t vlan_id = 0;
+	bool not_vlan = ((ptnr_vdev->tx_encap_type == htt_cmn_pkt_type_raw) ||
+			(htons(eh->ether_type) != ETH_P_8021Q));
+
+	if (qdf_unlikely(not_vlan))
+		return;
+	veh = (struct vlan_ethhdr *)eh;
+	vlan_id = (ntohs(veh->h_vlan_TCI) & VLAN_VID_MASK);
+
+	qdf_spin_lock_bh(&ptnr_vdev->mpass_peer_mutex);
+	TAILQ_FOREACH(txrx_peer, &ptnr_vdev->mpass_peer_list,
+		      mpass_peer_list_elem) {
+		if (vlan_id == txrx_peer->vlan_id) {
+			qdf_spin_unlock_bh(&ptnr_vdev->mpass_peer_mutex);
+			ptr->vlan_id = vlan_id;
+			return;
+		}
+	}
+	qdf_spin_unlock_bh(&ptnr_vdev->mpass_peer_mutex);
+}
+
+/**
+ * dp_tx_mlo_mcast_multipass_send() - send multipass MLO Mcast packets
+ * @be_vdev: Handle to DP be_vdev structure
+ * @ptnr_vdev: DP ptnr_vdev handle
+ * @arg: pointer to dp_mlo_mpass_ buf
+ *
+ * Return: None
+ */
+static void
+dp_tx_mlo_mcast_multipass_send(struct dp_vdev_be *be_vdev,
+			       struct dp_vdev *ptnr_vdev,
+			       void *arg)
+{
+	struct dp_mlo_mpass_buf *ptr = (struct dp_mlo_mpass_buf *)arg;
+	struct dp_tx_msdu_info_s msdu_info;
+	struct dp_vdev_be *be_ptnr_vdev = NULL;
+	qdf_nbuf_t  nbuf_clone;
+	uint16_t group_key = 0;
+
+	be_ptnr_vdev = dp_get_be_vdev_from_dp_vdev(ptnr_vdev);
+	if (be_vdev != be_ptnr_vdev) {
+		nbuf_clone = qdf_nbuf_clone(ptr->nbuf);
+		if (qdf_unlikely(!nbuf_clone)) {
+			dp_tx_debug("nbuf clone failed");
+			return;
+		}
+	} else {
+		nbuf_clone = ptr->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;
+
+	if (ptr->vlan_id == MULTIPASS_WITH_VLAN_ID) {
+		msdu_info.tid = HTT_TX_EXT_TID_INVALID;
+		HTT_TX_MSDU_EXT2_DESC_FLAG_VALID_KEY_FLAGS_SET(
+						msdu_info.meta_data[0], 1);
+	} else {
+		/* return when vlan map is not initialized */
+		if (!ptnr_vdev->iv_vlan_map)
+			return;
+		group_key = ptnr_vdev->iv_vlan_map[ptr->vlan_id];
+
+		/*
+		 * If group key is not installed, drop the frame.
+		 */
+
+		if (!group_key)
+			return;
+
+		dp_tx_remove_vlan_tag(ptnr_vdev, nbuf_clone);
+		dp_tx_add_groupkey_metadata(ptnr_vdev, &msdu_info, group_key);
+		msdu_info.exception_fw = 1;
+	}
+
+	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;
+	}
+}
+
+/**
+ * dp_tx_mlo_mcast_multipass_handler - If frame needs multipass processing
+ * @soc: DP soc handle
+ * @vdev: DP vdev handle
+ * @nbuf: nbuf to be enqueued
+ *
+ * Return: true if handling is done else false
+ */
+static bool
+dp_tx_mlo_mcast_multipass_handler(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);
+	qdf_nbuf_t nbuf_copy = NULL;
+	struct dp_mlo_mpass_buf mpass_buf;
+
+	memset(&mpass_buf, 0, sizeof(struct dp_mlo_mpass_buf));
+	mpass_buf.vlan_id = INVALID_VLAN_ID;
+	mpass_buf.nbuf = nbuf;
+
+	dp_tx_mlo_mcast_multipass_lookup(be_vdev, vdev, &mpass_buf);
+	if (mpass_buf.vlan_id == INVALID_VLAN_ID) {
+		dp_mcast_mlo_iter_ptnr_vdev(be_soc, be_vdev,
+					    dp_tx_mlo_mcast_multipass_lookup,
+					    &mpass_buf, DP_MOD_ID_TX);
+		/*
+		 * Do not drop the frame when vlan_id doesn't match.
+		 * Send the frame as it is.
+		 */
+		if (mpass_buf.vlan_id == INVALID_VLAN_ID)
+			return false;
+	}
+
+	/* AP can have classic clients, special clients &
+	 * classic repeaters.
+	 * 1. Classic clients & special client:
+	 *	Remove vlan header, find corresponding group key
+	 *	index, fill in metaheader and enqueue multicast
+	 *	frame to TCL.
+	 * 2. Classic repeater:
+	 *	Pass through to classic repeater with vlan tag
+	 *	intact without any group key index. Hardware
+	 *	will know which key to use to send frame to
+	 *	repeater.
+	 */
+	nbuf_copy = qdf_nbuf_copy(nbuf);
+
+	/*
+	 * Send multicast frame to special peers even
+	 * if pass through to classic repeater fails.
+	 */
+	if (nbuf_copy) {
+		struct dp_mlo_mpass_buf mpass_buf_copy = {0};
+
+		mpass_buf_copy.vlan_id = MULTIPASS_WITH_VLAN_ID;
+		mpass_buf_copy.nbuf = nbuf_copy;
+		/* send frame on partner vdevs */
+		dp_mcast_mlo_iter_ptnr_vdev(be_soc, be_vdev,
+					    dp_tx_mlo_mcast_multipass_send,
+					    &mpass_buf_copy, DP_MOD_ID_TX);
+
+		/* send frame on mcast primary vdev */
+		dp_tx_mlo_mcast_multipass_send(be_vdev, vdev, &mpass_buf_copy);
+
+		if (qdf_unlikely(be_vdev->seq_num > MAX_GSN_NUM))
+			be_vdev->seq_num = 0;
+		else
+			be_vdev->seq_num++;
+	}
+
+	dp_mcast_mlo_iter_ptnr_vdev(be_soc, be_vdev,
+				    dp_tx_mlo_mcast_multipass_send,
+				    &mpass_buf, DP_MOD_ID_TX);
+	dp_tx_mlo_mcast_multipass_send(be_vdev, vdev, &mpass_buf);
+
+	if (qdf_unlikely(be_vdev->seq_num > MAX_GSN_NUM))
+		be_vdev->seq_num = 0;
+	else
+		be_vdev->seq_num++;
+
+	return true;
+}
+#else
+static bool
+dp_tx_mlo_mcast_multipass_handler(struct dp_soc *soc, struct dp_vdev *vdev,
+				  qdf_nbuf_t nbuf)
+{
+	return false;
+}
+#endif
+
 void dp_tx_mcast_mlo_reinject_routing_set(struct dp_soc *soc, void *arg)
 {
 	hal_soc_handle_t hal_soc = soc->hal_soc;
@@ -488,6 +703,9 @@ void dp_tx_mlo_mcast_handler_be(struct dp_soc *soc,
 	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_unlikely(vdev->multipass_en) &&
+	    dp_tx_mlo_mcast_multipass_handler(soc, vdev, nbuf))
+		return;
 	/* send frame on partner vdevs */
 	dp_mcast_mlo_iter_ptnr_vdev(be_soc, be_vdev,
 				    dp_tx_mlo_mcast_pkt_send,

+ 7 - 0
dp/wifi3.0/dp_htt.c

@@ -549,6 +549,13 @@ int htt_srng_setup(struct htt_soc *soc, int mac_id,
 		    (lmac_id * HAL_MAX_RINGS_PER_LMAC))) {
 			htt_ring_id = HTT_HOST2_TO_FW_RXBUF_RING;
 			htt_ring_type = HTT_SW_TO_SW_RING;
+#ifdef IPA_WDI3_RX_TWO_PIPES
+		} else if (srng_params.ring_id ==
+		    (HAL_SRNG_WMAC1_SW2RXDMA0_BUF3 +
+		    (lmac_id * HAL_MAX_RINGS_PER_LMAC))) {
+			htt_ring_id = HTT_HOST3_TO_FW_RXBUF_RING;
+			htt_ring_type = HTT_SW_TO_SW_RING;
+#endif
 #endif
 #else
 		if (srng_params.ring_id ==

+ 531 - 5
dp/wifi3.0/dp_ipa.c

@@ -1165,6 +1165,33 @@ static void dp_rx_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev)
 	qdf_mem_free_sgtable(&ipa_res->rx_refill_ring.sgtable);
 }
 
+/*
+ * dp_rx_alt_ipa_uc_detach - free autonomy RX resources
+ * @soc: data path instance
+ * @pdev: core txrx pdev context
+ *
+ * This function will detach DP RX into main device context
+ * will free DP Rx resources.
+ *
+ * Return: none
+ */
+#ifdef IPA_WDI3_RX_TWO_PIPES
+static void dp_rx_alt_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev)
+{
+	struct dp_ipa_resources *ipa_res = &pdev->ipa_resource;
+
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	qdf_mem_free_sgtable(&ipa_res->rx_alt_rdy_ring.sgtable);
+	qdf_mem_free_sgtable(&ipa_res->rx_alt_refill_ring.sgtable);
+}
+#else
+static inline
+void dp_rx_alt_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev)
+{ }
+#endif
+
 int dp_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev)
 {
 	if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx))
@@ -1179,6 +1206,9 @@ int dp_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev)
 	/* RX resource detach */
 	dp_rx_ipa_uc_detach(soc, pdev);
 
+	/* Cleanup 2nd RX pipe resources */
+	dp_rx_alt_ipa_uc_detach(soc, pdev);
+
 	return QDF_STATUS_SUCCESS;	/* success */
 }
 
@@ -1356,6 +1386,85 @@ int dp_ipa_uc_attach(struct dp_soc *soc, struct dp_pdev *pdev)
 	return QDF_STATUS_SUCCESS;	/* success */
 }
 
+#ifdef IPA_WDI3_RX_TWO_PIPES
+/*
+ * dp_ipa_rx_alt_ring_resource_setup() - setup IPA 2nd RX ring resources
+ * @soc: data path SoC handle
+ * @pdev: data path pdev handle
+ *
+ * Return: none
+ */
+static
+void dp_ipa_rx_alt_ring_resource_setup(struct dp_soc *soc, struct dp_pdev *pdev)
+{
+	struct hal_soc *hal_soc = (struct hal_soc *)soc->hal_soc;
+	struct hal_srng *hal_srng;
+	struct hal_srng_params srng_params;
+	unsigned long addr_offset, dev_base_paddr;
+	qdf_dma_addr_t hp_addr;
+
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	dev_base_paddr =
+		(unsigned long)
+		((struct hif_softc *)(hal_soc->hif_handle))->mem_pa;
+
+	/* IPA REO_DEST Ring - HAL_SRNG_REO2SW3 */
+	hal_srng = (struct hal_srng *)
+			soc->reo_dest_ring[IPA_ALT_REO_DEST_RING_IDX].hal_srng;
+	hal_get_srng_params(hal_soc_to_hal_soc_handle(hal_soc),
+			    hal_srng_to_hal_ring_handle(hal_srng),
+			    &srng_params);
+
+	soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_base_paddr =
+						srng_params.ring_base_paddr;
+	soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_base_vaddr =
+						srng_params.ring_base_vaddr;
+	soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_size =
+		(srng_params.num_entries * srng_params.entry_size) << 2;
+	addr_offset = (unsigned long)(hal_srng->u.dst_ring.tp_addr) -
+		      (unsigned long)(hal_soc->dev_base_addr);
+	soc->ipa_uc_rx_rsc_alt.ipa_reo_tp_paddr =
+				(qdf_dma_addr_t)(addr_offset + dev_base_paddr);
+
+	dp_info("IPA REO_DEST Ring addr_offset=%x, dev_base_paddr=%x, tp_paddr=%x paddr=%pK vaddr=%pK size= %u(%u bytes)",
+		(unsigned int)addr_offset,
+		(unsigned int)dev_base_paddr,
+		(unsigned int)(soc->ipa_uc_rx_rsc_alt.ipa_reo_tp_paddr),
+		(void *)soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_base_paddr,
+		(void *)soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_base_vaddr,
+		srng_params.num_entries,
+		soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_size);
+
+	hal_srng = (struct hal_srng *)
+			pdev->rx_refill_buf_ring3.hal_srng;
+	hal_get_srng_params(hal_soc_to_hal_soc_handle(hal_soc),
+			    hal_srng_to_hal_ring_handle(hal_srng),
+			    &srng_params);
+	soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_base_paddr =
+		srng_params.ring_base_paddr;
+	soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_base_vaddr =
+		srng_params.ring_base_vaddr;
+	soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_size =
+		(srng_params.num_entries * srng_params.entry_size) << 2;
+	hp_addr = hal_srng_get_hp_addr(hal_soc_to_hal_soc_handle(hal_soc),
+				       hal_srng_to_hal_ring_handle(hal_srng));
+	soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_hp_paddr =
+		qdf_mem_paddr_from_dmaaddr(soc->osdev, hp_addr);
+
+	dp_info("IPA REFILL_BUF Ring hp_paddr=%x paddr=%pK vaddr=%pK size= %u(%u bytes)",
+		(unsigned int)(soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_hp_paddr),
+		(void *)soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_base_paddr,
+		(void *)soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_base_vaddr,
+		srng_params.num_entries,
+		soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_size);
+}
+#else
+static inline
+void dp_ipa_rx_alt_ring_resource_setup(struct dp_soc *soc, struct dp_pdev *pdev)
+{ }
+#endif
 /*
  * dp_ipa_ring_resource_setup() - setup IPA ring resources
  * @soc: data path SoC handle
@@ -1510,9 +1619,51 @@ int dp_ipa_ring_resource_setup(struct dp_soc *soc,
 
 	hal_reo_read_write_ctrl_ix(soc->hal_soc, false, &ix0, NULL, NULL, NULL);
 
+	dp_ipa_rx_alt_ring_resource_setup(soc, pdev);
 	return 0;
 }
 
+#ifdef IPA_WDI3_RX_TWO_PIPES
+/*
+ * dp_ipa_rx_alt_ring_get_resource() - get IPA 2nd RX ring resources
+ * @pdev: data path pdev handle
+ *
+ * Return: Success if resourece is found
+ */
+static QDF_STATUS dp_ipa_rx_alt_ring_get_resource(struct dp_pdev *pdev)
+{
+	struct dp_soc *soc = pdev->soc;
+	struct dp_ipa_resources *ipa_res = &pdev->ipa_resource;
+
+	if (!wlan_ipa_is_vlan_enabled())
+		return QDF_STATUS_SUCCESS;
+
+	dp_ipa_get_shared_mem_info(soc->osdev, &ipa_res->rx_alt_rdy_ring,
+				   soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_base_vaddr,
+				   soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_base_paddr,
+				   soc->ipa_uc_rx_rsc_alt.ipa_reo_ring_size);
+
+	dp_ipa_get_shared_mem_info(
+			soc->osdev, &ipa_res->rx_alt_refill_ring,
+			soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_base_vaddr,
+			soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_base_paddr,
+			soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_ring_size);
+
+	if (!qdf_mem_get_dma_addr(soc->osdev,
+				  &ipa_res->rx_alt_rdy_ring.mem_info) ||
+	    !qdf_mem_get_dma_addr(soc->osdev,
+				  &ipa_res->rx_alt_refill_ring.mem_info))
+		return QDF_STATUS_E_FAILURE;
+
+	return QDF_STATUS_SUCCESS;
+}
+#else
+static inline QDF_STATUS dp_ipa_rx_alt_ring_get_resource(struct dp_pdev *pdev)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 QDF_STATUS dp_ipa_get_resource(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
 {
 	struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
@@ -1564,6 +1715,9 @@ QDF_STATUS dp_ipa_get_resource(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
 	if (dp_ipa_tx_alt_ring_get_resource(pdev))
 		return QDF_STATUS_E_FAILURE;
 
+	if (dp_ipa_rx_alt_ring_get_resource(pdev))
+		return QDF_STATUS_E_FAILURE;
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -1574,6 +1728,70 @@ QDF_STATUS dp_ipa_get_resource(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
 		dp_ipa_set_tx_doorbell_paddr(soc, ipa_res)
 #endif
 
+#ifdef IPA_WDI3_RX_TWO_PIPES
+/*
+ * dp_ipa_map_rx_alt_ring_doorbell_paddr() - Map 2nd rx ring doorbell paddr
+ * @pdev: data path pdev handle
+ *
+ * Return: none
+ */
+static void dp_ipa_map_rx_alt_ring_doorbell_paddr(struct dp_pdev *pdev)
+{
+	struct dp_ipa_resources *ipa_res = &pdev->ipa_resource;
+	uint32_t rx_ready_doorbell_dmaaddr;
+	struct dp_soc *soc = pdev->soc;
+	struct hal_srng *reo_srng = (struct hal_srng *)
+			soc->reo_dest_ring[IPA_ALT_REO_DEST_RING_IDX].hal_srng;
+	int ret = 0;
+
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	if (qdf_mem_smmu_s1_enabled(soc->osdev)) {
+		ret = pld_smmu_map(soc->osdev->dev,
+				   ipa_res->rx_alt_ready_doorbell_paddr,
+				   &rx_ready_doorbell_dmaaddr,
+				   sizeof(uint32_t));
+		ipa_res->rx_alt_ready_doorbell_paddr =
+					rx_ready_doorbell_dmaaddr;
+		qdf_assert_always(!ret);
+	}
+
+	hal_srng_dst_set_hp_paddr_confirm(reo_srng,
+					  ipa_res->rx_alt_ready_doorbell_paddr);
+}
+
+/*
+ * dp_ipa_unmap_rx_alt_ring_doorbell_paddr() - Unmap 2nd rx ring doorbell paddr
+ * @pdev: data path pdev handle
+ *
+ * Return: none
+ */
+static void dp_ipa_unmap_rx_alt_ring_doorbell_paddr(struct dp_pdev *pdev)
+{
+	struct dp_ipa_resources *ipa_res = &pdev->ipa_resource;
+	struct dp_soc *soc = pdev->soc;
+	int ret = 0;
+
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	if (!qdf_mem_smmu_s1_enabled(soc->osdev))
+		return;
+
+	ret = pld_smmu_unmap(soc->osdev->dev,
+			     ipa_res->rx_alt_ready_doorbell_paddr,
+			     sizeof(uint32_t));
+	qdf_assert_always(!ret);
+}
+#else
+static inline void dp_ipa_map_rx_alt_ring_doorbell_paddr(struct dp_pdev *pdev)
+{ }
+
+static inline void dp_ipa_unmap_rx_alt_ring_doorbell_paddr(struct dp_pdev *pdev)
+{ }
+#endif
+
 QDF_STATUS dp_ipa_set_doorbell_paddr(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
 {
 	struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
@@ -1593,6 +1811,7 @@ QDF_STATUS dp_ipa_set_doorbell_paddr(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
 		return QDF_STATUS_SUCCESS;
 
 	dp_ipa_map_ring_doorbell_paddr(pdev);
+	dp_ipa_map_rx_alt_ring_doorbell_paddr(pdev);
 
 	DP_IPA_SET_TX_DB_PADDR(soc, ipa_res);
 
@@ -1776,7 +1995,10 @@ QDF_STATUS dp_ipa_enable_autonomy(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
 	ix_map[0] = REO_REMAP_SW1;
 	ix_map[1] = REO_REMAP_SW4;
 	ix_map[2] = REO_REMAP_SW1;
-	ix_map[3] = REO_REMAP_SW4;
+	if (wlan_ipa_is_vlan_enabled())
+		ix_map[3] = REO_REMAP_SW3;
+	else
+		ix_map[3] = REO_REMAP_SW4;
 	ix_map[4] = REO_REMAP_SW4;
 	ix_map[5] = REO_REMAP_RELEASE;
 	ix_map[6] = REO_REMAP_FW;
@@ -2064,6 +2286,208 @@ dp_ipa_wdi_rx_smmu_params(struct dp_soc *soc,
 		soc->rx_pkt_tlv_size + L3_HEADER_PADDING;
 }
 
+#ifdef IPA_WDI3_RX_TWO_PIPES
+/*
+ * dp_ipa_wdi_rx_alt_pipe_smmu_params() - Setup 2nd rx pipe smmu params
+ * @soc: data path soc handle
+ * @ipa_res: ipa resource pointer
+ * @rx_smmu: smmu pipe info handle
+ * @over_gsi: flag for IPA offload over gsi
+ * @hdl: ipa registered handle
+ *
+ * Return: none
+ */
+static void
+dp_ipa_wdi_rx_alt_pipe_smmu_params(struct dp_soc *soc,
+				   struct dp_ipa_resources *ipa_res,
+				   qdf_ipa_wdi_pipe_setup_info_smmu_t *rx_smmu,
+				   bool over_gsi,
+				   qdf_ipa_wdi_hdl_t hdl)
+{
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	if (over_gsi) {
+		if (hdl == DP_IPA_HDL_FIRST)
+			QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(rx_smmu) =
+				IPA_CLIENT_WLAN2_PROD1;
+		else if (hdl == DP_IPA_HDL_SECOND)
+			QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(rx_smmu) =
+				IPA_CLIENT_WLAN3_PROD1;
+	} else {
+		QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(rx_smmu) =
+					IPA_CLIENT_WLAN1_PROD;
+	}
+
+	qdf_mem_copy(&QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_BASE(rx_smmu),
+		     &ipa_res->rx_alt_rdy_ring.sgtable,
+		     sizeof(sgtable_t));
+	QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_SIZE(rx_smmu) =
+		qdf_mem_get_dma_size(soc->osdev,
+				     &ipa_res->rx_alt_rdy_ring.mem_info);
+	/* REO Tail Pointer Address */
+	QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_DOORBELL_PA(rx_smmu) =
+		soc->ipa_uc_rx_rsc_alt.ipa_reo_tp_paddr;
+	QDF_IPA_WDI_SETUP_INFO_SMMU_IS_TXR_RN_DB_PCIE_ADDR(rx_smmu) = true;
+
+	qdf_mem_copy(&QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_BASE(rx_smmu),
+		     &ipa_res->rx_alt_refill_ring.sgtable,
+		     sizeof(sgtable_t));
+	QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_SIZE(rx_smmu) =
+		qdf_mem_get_dma_size(soc->osdev,
+				     &ipa_res->rx_alt_refill_ring.mem_info);
+
+	/* FW Head Pointer Address */
+	QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_DOORBELL_PA(rx_smmu) =
+		soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_hp_paddr;
+	QDF_IPA_WDI_SETUP_INFO_SMMU_IS_EVT_RN_DB_PCIE_ADDR(rx_smmu) = false;
+
+	QDF_IPA_WDI_SETUP_INFO_SMMU_PKT_OFFSET(rx_smmu) =
+		soc->rx_pkt_tlv_size + L3_HEADER_PADDING;
+}
+
+/*
+ * dp_ipa_wdi_rx_alt_pipe_smmu_params() - Setup 2nd rx pipe params
+ * @soc: data path soc handle
+ * @ipa_res: ipa resource pointer
+ * @rx: pipe info handle
+ * @over_gsi: flag for IPA offload over gsi
+ * @hdl: ipa registered handle
+ *
+ * Return: none
+ */
+static void dp_ipa_wdi_rx_alt_pipe_params(struct dp_soc *soc,
+					  struct dp_ipa_resources *ipa_res,
+					  qdf_ipa_wdi_pipe_setup_info_t *rx,
+					  bool over_gsi,
+					  qdf_ipa_wdi_hdl_t hdl)
+{
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	if (over_gsi) {
+		if (hdl == DP_IPA_HDL_FIRST)
+			QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(rx_smmu) =
+				IPA_CLIENT_WLAN2_PROD1;
+		else if (hdl == DP_IPA_HDL_SECOND)
+			QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(rx_smmu) =
+				IPA_CLIENT_WLAN3_PROD1;
+	} else {
+		QDF_IPA_WDI_SETUP_INFO_CLIENT(rx) =
+					IPA_CLIENT_WLAN1_PROD;
+	}
+
+	QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(rx) =
+		qdf_mem_get_dma_addr(soc->osdev,
+				     &ipa_res->rx_alt_rdy_ring.mem_info);
+	QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(rx) =
+		qdf_mem_get_dma_size(soc->osdev,
+				     &ipa_res->rx_alt_rdy_ring.mem_info);
+
+	/* REO Tail Pointer Address */
+	QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(rx) =
+		soc->ipa_uc_rx_rsc_alt.ipa_reo_tp_paddr;
+	QDF_IPA_WDI_SETUP_INFO_IS_TXR_RN_DB_PCIE_ADDR(rx) = true;
+
+	QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(rx) =
+		qdf_mem_get_dma_addr(soc->osdev,
+				     &ipa_res->rx_alt_refill_ring.mem_info);
+	QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(rx) =
+		qdf_mem_get_dma_size(soc->osdev,
+				     &ipa_res->rx_alt_refill_ring.mem_info);
+
+	/* FW Head Pointer Address */
+	QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(rx) =
+		soc->ipa_uc_rx_rsc_alt.ipa_rx_refill_buf_hp_paddr;
+	QDF_IPA_WDI_SETUP_INFO_IS_EVT_RN_DB_PCIE_ADDR(rx) = false;
+
+	QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(rx) =
+		soc->rx_pkt_tlv_size + L3_HEADER_PADDING;
+}
+
+/*
+ * dp_ipa_setup_rx_alt_pipe() - Setup 2nd rx pipe for IPA offload
+ * @soc: data path soc handle
+ * @res: ipa resource pointer
+ * @in: pipe in handle
+ * @over_gsi: flag for IPA offload over gsi
+ * @hdl: ipa registered handle
+ *
+ * Return: none
+ */
+static void dp_ipa_setup_rx_alt_pipe(struct dp_soc *soc,
+				     struct dp_ipa_resources *res,
+				     qdf_ipa_wdi_conn_in_params_t *in,
+				     bool over_gsi,
+				     qdf_ipa_wdi_hdl_t hdl)
+{
+	qdf_ipa_wdi_pipe_setup_info_smmu_t *rx_smmu = NULL;
+	qdf_ipa_wdi_pipe_setup_info_t *rx = NULL;
+	qdf_ipa_ep_cfg_t *rx_cfg;
+
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	QDF_IPA_WDI_CONN_IN_PARAMS_IS_RX1_USED(in) = true;
+	if (qdf_mem_smmu_s1_enabled(soc->osdev)) {
+		rx_smmu = &QDF_IPA_WDI_CONN_IN_PARAMS_RX_ALT_SMMU(in);
+		rx_cfg = &QDF_IPA_WDI_SETUP_INFO_SMMU_EP_CFG(rx_smmu);
+		dp_ipa_wdi_rx_alt_pipe_smmu_params(soc, res, rx_smmu,
+						   over_gsi, hdl);
+	} else {
+		rx = &QDF_IPA_WDI_CONN_IN_PARAMS_RX_ALT(in);
+		rx_cfg = &QDF_IPA_WDI_SETUP_INFO_SMMU_EP_CFG(rx);
+		dp_ipa_wdi_rx_alt_pipe_params(soc, res, rx, over_gsi, hdl);
+	}
+
+	QDF_IPA_EP_CFG_NAT_EN(rx_cfg) = IPA_BYPASS_NAT;
+	/* Update with wds len(96) + 4 if wds support is enabled */
+	if (ucfg_ipa_is_wds_enabled())
+		QDF_IPA_EP_CFG_HDR_LEN(rx_cfg) = DP_IPA_UC_WLAN_RX_HDR_LEN_AST_VLAN;
+	else
+		QDF_IPA_EP_CFG_HDR_LEN(rx_cfg) = DP_IPA_UC_WLAN_TX_VLAN_HDR_LEN;
+	QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE_VALID(rx_cfg) = 1;
+	QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE(rx_cfg) = 0;
+	QDF_IPA_EP_CFG_HDR_ADDITIONAL_CONST_LEN(rx_cfg) = 0;
+	QDF_IPA_EP_CFG_HDR_OFST_METADATA_VALID(rx_cfg) = 0;
+	QDF_IPA_EP_CFG_HDR_METADATA_REG_VALID(rx_cfg) = 1;
+	QDF_IPA_EP_CFG_MODE(rx_cfg) = IPA_BASIC;
+	QDF_IPA_EP_CFG_HDR_LITTLE_ENDIAN(rx_cfg) = true;
+}
+
+/*
+ * dp_ipa_set_rx_alt_pipe_db() - Setup 2nd rx pipe doorbell
+ * @res: ipa resource pointer
+ * @out: pipe out handle
+ *
+ * Return: none
+ */
+static void dp_ipa_set_rx_alt_pipe_db(struct dp_ipa_resources *res,
+				      qdf_ipa_wdi_conn_out_params_t *out)
+{
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	res->rx_alt_ready_doorbell_paddr =
+			QDF_IPA_WDI_CONN_OUT_PARAMS_RX_ALT_UC_DB_PA(out);
+	dp_debug("Setting DB 0x%x for RX alt pipe",
+		 res->rx_alt_ready_doorbell_paddr);
+}
+#else
+static inline
+void dp_ipa_setup_rx_alt_pipe(struct dp_soc *soc,
+			      struct dp_ipa_resources *res,
+			      qdf_ipa_wdi_conn_in_params_t *in,
+			      bool over_gsi,
+			      qdf_ipa_wdi_hdl_t hdl)
+{ }
+
+static inline
+void dp_ipa_set_rx_alt_pipe_db(struct dp_ipa_resources *res,
+			       qdf_ipa_wdi_conn_out_params_t *out)
+{ }
+#endif
+
 QDF_STATUS dp_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
 			void *ipa_i2w_cb, void *ipa_w2i_cb,
 			void *ipa_wdi_meter_notifier_cb,
@@ -2174,6 +2598,9 @@ QDF_STATUS dp_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
 	else
 		dp_ipa_wdi_rx_params(soc, ipa_res, rx, over_gsi);
 
+	/* setup 2nd rx pipe */
+	dp_ipa_setup_rx_alt_pipe(soc, ipa_res, pipe_in, over_gsi, id);
+
 	QDF_IPA_WDI_CONN_IN_PARAMS_NOTIFY(pipe_in) = ipa_w2i_cb;
 	QDF_IPA_WDI_CONN_IN_PARAMS_PRIV(pipe_in) = ipa_priv;
 	QDF_IPA_WDI_CONN_IN_PARAMS_HANDLE(pipe_in) = hdl;
@@ -2196,6 +2623,7 @@ QDF_STATUS dp_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
 		(unsigned int)QDF_IPA_WDI_CONN_OUT_PARAMS_RX_UC_DB_PA(&pipe_out));
 
 	dp_ipa_set_pipe_db(ipa_res, &pipe_out);
+	dp_ipa_set_rx_alt_pipe_db(ipa_res, &pipe_out);
 
 	ipa_res->is_db_ddr_mapped =
 		QDF_IPA_WDI_CONN_OUT_PARAMS_IS_DB_DDR_MAPPED(&pipe_out);
@@ -2209,6 +2637,64 @@ QDF_STATUS dp_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef IPA_WDI3_RX_TWO_PIPES
+/*
+ * dp_ipa_set_rx1_used() - Set rx1 used flag for 2nd rx offload ring
+ * @in: pipe in handle
+ *
+ * Return: none
+ */
+static inline
+void dp_ipa_set_rx1_used(qdf_ipa_wdi_reg_intf_in_params_t *in)
+{
+	QDF_IPA_WDI_REG_INTF_IN_PARAMS_IS_RX1_USED(in) = true;
+}
+
+/*
+ * dp_ipa_set_v4_vlan_hdr() - Set v4 vlan hdr
+ * @in: pipe in handle
+ * hdr: pointer to hdr
+ *
+ * Return: none
+ */
+static inline
+void dp_ipa_set_v4_vlan_hdr(qdf_ipa_wdi_reg_intf_in_params_t *in,
+			    qdf_ipa_wdi_hdr_info_t *hdr)
+{
+	qdf_mem_copy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(in)[IPA_IP_v4_VLAN]),
+		     hdr, sizeof(qdf_ipa_wdi_hdr_info_t));
+}
+
+/*
+ * dp_ipa_set_v6_vlan_hdr() - Set v6 vlan hdr
+ * @in: pipe in handle
+ * hdr: pointer to hdr
+ *
+ * Return: none
+ */
+static inline
+void dp_ipa_set_v6_vlan_hdr(qdf_ipa_wdi_reg_intf_in_params_t *in,
+			    qdf_ipa_wdi_hdr_info_t *hdr)
+{
+	qdf_mem_copy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(in)[IPA_IP_v6_VLAN]),
+		     hdr, sizeof(qdf_ipa_wdi_hdr_info_t));
+}
+#else
+static inline
+void dp_ipa_set_rx1_used(qdf_ipa_wdi_reg_intf_in_params_t *in)
+{ }
+
+static inline
+void dp_ipa_set_v4_vlan_hdr(qdf_ipa_wdi_reg_intf_in_params_t *in,
+			    qdf_ipa_wdi_hdr_info_t *hdr)
+{ }
+
+static inline
+void dp_ipa_set_v6_vlan_hdr(qdf_ipa_wdi_reg_intf_in_params_t *in,
+			    qdf_ipa_wdi_hdr_info_t *hdr)
+{ }
+#endif
+
 /**
  * dp_ipa_setup_iface() - Setup IPA header and register interface
  * @ifname: Interface name
@@ -2231,6 +2717,8 @@ QDF_STATUS dp_ipa_setup_iface(char *ifname, uint8_t *mac_addr,
 	qdf_ipa_wdi_hdr_info_t hdr_info;
 	struct dp_ipa_uc_tx_hdr uc_tx_hdr;
 	struct dp_ipa_uc_tx_hdr uc_tx_hdr_v6;
+	struct dp_ipa_uc_tx_vlan_hdr uc_tx_vlan_hdr;
+	struct dp_ipa_uc_tx_vlan_hdr uc_tx_vlan_hdr_v6;
 	int ret = -EINVAL;
 
 	qdf_mem_zero(&in, sizeof(qdf_ipa_wdi_reg_intf_in_params_t));
@@ -2258,6 +2746,7 @@ QDF_STATUS dp_ipa_setup_iface(char *ifname, uint8_t *mac_addr,
 	QDF_IPA_WDI_REG_INTF_IN_PARAMS_META_DATA_MASK(&in) = WLAN_IPA_META_DATA_MASK;
 	QDF_IPA_WDI_REG_INTF_IN_PARAMS_HANDLE(&in) = hdl;
 	dp_ipa_setup_iface_session_id(&in, session_id);
+	dp_debug("registering for session_id: %u", session_id);
 
 	/* IPV6 header */
 	if (is_ipv6_enabled) {
@@ -2269,14 +2758,50 @@ QDF_STATUS dp_ipa_setup_iface(char *ifname, uint8_t *mac_addr,
 			     &hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t));
 	}
 
-	dp_debug("registering for session_id: %u", session_id);
+	if (wlan_ipa_is_vlan_enabled()) {
+		/* Add vlan specific headers if vlan supporti is enabled */
+		qdf_mem_zero(&hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t));
+		dp_ipa_set_rx1_used(&in);
+		qdf_ether_addr_copy(uc_tx_vlan_hdr.eth.h_source, mac_addr);
+		/* IPV4 Vlan header */
+		uc_tx_vlan_hdr.eth.h_vlan_proto = qdf_htons(ETH_P_8021Q);
+		uc_tx_vlan_hdr.eth.h_vlan_encapsulated_proto = qdf_htons(ETH_P_IP);
+
+		QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) =
+				(uint8_t *)&uc_tx_vlan_hdr;
+		QDF_IPA_WDI_HDR_INFO_HDR_LEN(&hdr_info) =
+				DP_IPA_UC_WLAN_TX_VLAN_HDR_LEN;
+		if (ucfg_ipa_is_wds_enabled())
+			QDF_IPA_WDI_HDR_INFO_HDR_TYPE(&hdr_info) =
+					IPA_HDR_L2_802_1Q_AST;
+		else
+			QDF_IPA_WDI_HDR_INFO_HDR_TYPE(&hdr_info) =
+					IPA_HDR_L2_802_1Q;
+
+		QDF_IPA_WDI_HDR_INFO_DST_MAC_ADDR_OFFSET(&hdr_info) =
+			DP_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
+
+		dp_ipa_set_v4_vlan_hdr(&in, &hdr_info);
+
+		/* IPV6 Vlan header */
+		if (is_ipv6_enabled) {
+			qdf_mem_copy(&uc_tx_vlan_hdr_v6, &uc_tx_vlan_hdr,
+				     DP_IPA_UC_WLAN_TX_VLAN_HDR_LEN);
+			uc_tx_vlan_hdr_v6.eth.h_vlan_proto =
+					qdf_htons(ETH_P_8021Q);
+			uc_tx_vlan_hdr_v6.eth.h_vlan_encapsulated_proto =
+					qdf_htons(ETH_P_IPV6);
+			QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) =
+					(uint8_t *)&uc_tx_vlan_hdr_v6;
+			dp_ipa_set_v6_vlan_hdr(&in, &hdr_info);
+		}
+	}
 
 	ret = qdf_ipa_wdi_reg_intf(&in);
-
 	if (ret) {
 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-		    "%s: ipa_wdi_reg_intf: register IPA interface falied: ret=%d",
-		    __func__, ret);
+			  "%s: ipa_wdi_reg_intf: register IPA interface falied: ret=%d",
+			  __func__, ret);
 		return QDF_STATUS_E_FAILURE;
 	}
 
@@ -2581,6 +3106,7 @@ QDF_STATUS dp_ipa_cleanup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
 	}
 
 	dp_ipa_unmap_ring_doorbell_paddr(pdev);
+	dp_ipa_unmap_rx_alt_ring_doorbell_paddr(pdev);
 exit:
 	return status;
 }

+ 13 - 0
dp/wifi3.0/dp_ipa.h

@@ -35,6 +35,9 @@
 
 #define IPA_RX_REFILL_BUF_RING_IDX	2
 
+#define IPA_ALT_REO_DEST_RING_IDX	2
+#define IPA_RX_ALT_REFILL_BUF_RING_IDX	3
+
 /* Adding delay before disabling ipa pipes if any Tx Completions are pending */
 #define TX_COMP_DRAIN_WAIT_MS	50
 #define TX_COMP_DRAIN_WAIT_TIMEOUT_MS	100
@@ -64,6 +67,14 @@ struct dp_ipa_uc_tx_hdr {
 	struct ethhdr eth;
 } __packed;
 
+/**
+ * struct dp_ipa_uc_tx_hdr - full tx header registered to IPA hardware
+ * @eth:     ether II header
+ */
+struct dp_ipa_uc_tx_vlan_hdr {
+	struct vlan_ethhdr eth;
+} __packed;
+
 /**
  * struct dp_ipa_uc_rx_hdr - full rx header registered to IPA hardware
  * @eth:     ether II header
@@ -73,11 +84,13 @@ struct dp_ipa_uc_rx_hdr {
 } __packed;
 
 #define DP_IPA_UC_WLAN_TX_HDR_LEN      sizeof(struct dp_ipa_uc_tx_hdr)
+#define DP_IPA_UC_WLAN_TX_VLAN_HDR_LEN sizeof(struct dp_ipa_uc_tx_vlan_hdr)
 #define DP_IPA_UC_WLAN_RX_HDR_LEN      sizeof(struct dp_ipa_uc_rx_hdr)
 /* 28 <bytes of rx_msdu_end_tlv> + 16 <bytes of attn tlv> +
  * 52 <bytes of rx_mpdu_start_tlv> + <L2 Header>
  */
 #define DP_IPA_UC_WLAN_RX_HDR_LEN_AST  110
+#define DP_IPA_UC_WLAN_RX_HDR_LEN_AST_VLAN 114
 #define DP_IPA_UC_WLAN_HDR_DES_MAC_OFFSET	0
 
 #define DP_IPA_HDL_INVALID	0xFF

+ 216 - 18
dp/wifi3.0/dp_main.c

@@ -17,6 +17,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <wlan_ipa_obj_mgmt_api.h>
 #include <qdf_types.h>
 #include <qdf_lock.h>
 #include <qdf_net_types.h>
@@ -4093,6 +4094,53 @@ static void dp_soc_disable_unused_mac_intr_mask(struct dp_soc *soc,
 					      group_number, 0x0);
 }
 
+#ifdef IPA_OFFLOAD
+#ifdef IPA_WDI3_RX_TWO_PIPES
+/*
+ * dp_soc_reset_ipa_vlan_intr_mask() - reset interrupt mask for IPA offloaded
+ * ring for vlan tagged traffic
+ * @dp_soc - DP Soc handle
+ *
+ * Return: Return void
+ */
+static void dp_soc_reset_ipa_vlan_intr_mask(struct dp_soc *soc)
+{
+	uint8_t *grp_mask = NULL;
+	int group_number, mask;
+
+	if (!wlan_ipa_is_vlan_enabled())
+		return;
+
+	grp_mask = &soc->wlan_cfg_ctx->int_rx_ring_mask[0];
+
+	group_number = dp_srng_find_ring_in_mask(IPA_ALT_REO_DEST_RING_IDX, grp_mask);
+	if (group_number < 0) {
+		dp_init_debug("%pK: ring not part of any group; ring_type: %d,ring_num %d",
+			      soc, REO_DST, IPA_ALT_REO_DEST_RING_IDX);
+		return;
+	}
+
+	mask =  wlan_cfg_get_rx_ring_mask(soc->wlan_cfg_ctx, group_number);
+
+	/* reset the interrupt mask for offloaded ring */
+	mask &= (~(1 << IPA_ALT_REO_DEST_RING_IDX));
+
+	/*
+	 * set the interrupt mask to zero for rx offloaded radio.
+	 */
+	wlan_cfg_set_rx_ring_mask(soc->wlan_cfg_ctx, group_number, mask);
+}
+#else
+static inline
+void dp_soc_reset_ipa_vlan_intr_mask(struct dp_soc *soc)
+{ }
+#endif /* IPA_WDI3_RX_TWO_PIPES */
+#else
+static inline
+void dp_soc_reset_ipa_vlan_intr_mask(struct dp_soc *soc)
+{ }
+#endif /* IPA_OFFLOAD */
+
 /*
  * dp_soc_reset_intr_mask() - reset interrupt mask
  * @dp_soc - DP Soc handle
@@ -4249,10 +4297,21 @@ bool dp_reo_remap_config(struct dp_soc *soc, uint32_t *remap0,
 		break;
 
 	case CDP_ARCH_TYPE_LI:
-		hal_compute_reo_remap_ix2_ix3(soc->hal_soc, ring,
-					      soc->num_reo_dest_rings -
-					      USE_1_IPA_RX_REO_RING, remap1,
-					      remap2);
+		if (wlan_ipa_is_vlan_enabled()) {
+			hal_compute_reo_remap_ix2_ix3(
+					soc->hal_soc, ring,
+					soc->num_reo_dest_rings -
+					USE_2_IPA_RX_REO_RINGS, remap1,
+					remap2);
+
+		} else {
+			hal_compute_reo_remap_ix2_ix3(
+					soc->hal_soc, ring,
+					soc->num_reo_dest_rings -
+					USE_1_IPA_RX_REO_RING, remap1,
+					remap2);
+		}
+
 		hal_compute_reo_remap_ix0(soc->hal_soc, remap0);
 		break;
 	default:
@@ -5043,6 +5102,101 @@ static int dp_setup_ipa_rx_refill_buf_ring(struct dp_soc *soc,
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef IPA_WDI3_RX_TWO_PIPES
+static int dp_setup_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					       struct dp_pdev *pdev)
+{
+	struct wlan_cfg_dp_soc_ctxt *soc_cfg_ctx;
+	int entries;
+
+	if (wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx) &&
+	    wlan_ipa_is_vlan_enabled()) {
+		soc_cfg_ctx = soc->wlan_cfg_ctx;
+		entries =
+			wlan_cfg_get_dp_soc_rxdma_refill_ring_size(soc_cfg_ctx);
+
+		/* Setup second Rx refill buffer ring */
+		if (dp_srng_alloc(soc, &pdev->rx_refill_buf_ring3, RXDMA_BUF,
+				  entries, 0)) {
+			dp_init_err("%pK: alloc failed for 3rd rx refill ring",
+				    soc);
+			return QDF_STATUS_E_FAILURE;
+		}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static int dp_init_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					      struct dp_pdev *pdev)
+{
+	if (wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx) &&
+	    wlan_ipa_is_vlan_enabled()) {
+		if (dp_srng_init(soc, &pdev->rx_refill_buf_ring3, RXDMA_BUF,
+				 IPA_RX_ALT_REFILL_BUF_RING_IDX,
+				 pdev->pdev_id)) {
+			dp_init_err("%pK: init failed for 3rd rx refill ring",
+				    soc);
+			return QDF_STATUS_E_FAILURE;
+		}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static void dp_deinit_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+						 struct dp_pdev *pdev)
+{
+	if (wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx) &&
+	    wlan_ipa_is_vlan_enabled())
+		dp_srng_deinit(soc, &pdev->rx_refill_buf_ring3, RXDMA_BUF, 0);
+}
+
+static void dp_free_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					       struct dp_pdev *pdev)
+{
+	if (wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx) &&
+	    wlan_ipa_is_vlan_enabled())
+		dp_srng_free(soc, &pdev->rx_refill_buf_ring3);
+}
+#else
+static int dp_setup_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					       struct dp_pdev *pdev)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static int dp_init_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					      struct dp_pdev *pdev)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static void dp_deinit_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+						 struct dp_pdev *pdev)
+{
+}
+
+static void dp_free_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					       struct dp_pdev *pdev)
+{
+}
+#endif
+
+/**
+ * dp_deinit_ipa_rx_refill_buf_ring - deinit second Rx refill buffer ring
+ * @soc: data path instance
+ * @pdev: core txrx pdev context
+ *
+ * Return: void
+ */
+static void dp_deinit_ipa_rx_refill_buf_ring(struct dp_soc *soc,
+					     struct dp_pdev *pdev)
+{
+	if (wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx))
+		dp_srng_deinit(soc, &pdev->rx_refill_buf_ring2, RXDMA_BUF, 0);
+}
+
 /**
  * dp_init_ipa_rx_refill_buf_ring - Init second Rx refill buffer ring
  * @soc: data path instance
@@ -5062,21 +5216,13 @@ static int dp_init_ipa_rx_refill_buf_ring(struct dp_soc *soc,
 			return QDF_STATUS_E_FAILURE;
 		}
 	}
-	return QDF_STATUS_SUCCESS;
-}
 
-/**
- * dp_deinit_ipa_rx_refill_buf_ring - deinit second Rx refill buffer ring
- * @soc: data path instance
- * @pdev: core txrx pdev context
- *
- * Return: void
- */
-static void dp_deinit_ipa_rx_refill_buf_ring(struct dp_soc *soc,
-					     struct dp_pdev *pdev)
-{
-	if (wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx))
-		dp_srng_deinit(soc, &pdev->rx_refill_buf_ring2, RXDMA_BUF, 0);
+	if (dp_init_ipa_rx_alt_refill_buf_ring(soc, pdev)) {
+		dp_deinit_ipa_rx_refill_buf_ring(soc, pdev);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return QDF_STATUS_SUCCESS;
 }
 
 /**
@@ -5114,6 +5260,22 @@ static void dp_free_ipa_rx_refill_buf_ring(struct dp_soc *soc,
 					   struct dp_pdev *pdev)
 {
 }
+
+static int dp_setup_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					       struct dp_pdev *pdev)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static void dp_deinit_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+						 struct dp_pdev *pdev)
+{
+}
+
+static void dp_free_ipa_rx_alt_refill_buf_ring(struct dp_soc *soc,
+					       struct dp_pdev *pdev)
+{
+}
 #endif
 
 #ifdef DP_TX_HW_DESC_HISTORY
@@ -5439,7 +5601,17 @@ QDF_STATUS dp_pdev_attach_wifi3(struct cdp_soc_t *txrx_soc,
 
 	soc->arch_ops.txrx_pdev_attach(pdev, params);
 
+	/* Setup third Rx refill buffer ring */
+	if (dp_setup_ipa_rx_alt_refill_buf_ring(soc, pdev)) {
+		dp_init_err("%pK: dp_srng_alloc failed rxrefill3 ring",
+			    soc);
+		goto fail6;
+	}
+
 	return QDF_STATUS_SUCCESS;
+
+fail6:
+	dp_monitor_pdev_detach(pdev);
 fail5:
 	dp_rx_pdev_desc_pool_free(pdev);
 fail4:
@@ -5591,6 +5763,7 @@ static void dp_pdev_deinit(struct cdp_pdev *txrx_pdev, int force)
 	dp_pdev_srng_deinit(pdev);
 
 	dp_ipa_uc_detach(pdev->soc, pdev);
+	dp_deinit_ipa_rx_alt_refill_buf_ring(pdev->soc, pdev);
 	dp_deinit_ipa_rx_refill_buf_ring(pdev->soc, pdev);
 	dp_rxdma_ring_cleanup(pdev->soc, pdev);
 
@@ -5691,6 +5864,7 @@ static void dp_pdev_detach(struct cdp_pdev *txrx_pdev, int force)
 	dp_monitor_pdev_detach(pdev);
 	dp_rxdma_ring_free(pdev);
 	dp_free_ipa_rx_refill_buf_ring(soc, pdev);
+	dp_free_ipa_rx_alt_refill_buf_ring(soc, pdev);
 	dp_pdev_srng_free(pdev);
 
 	soc->pdev_count--;
@@ -6016,6 +6190,25 @@ dp_htt_setup_rxdma_err_dst_ring(struct dp_soc *soc, int mac_id,
 			       RXDMA_DST);
 }
 
+#ifdef IPA_WDI3_RX_TWO_PIPES
+static inline
+void dp_rxdma_setup_refill_ring3(struct dp_soc *soc,
+				 struct dp_pdev *pdev,
+				 uint8_t idx)
+{
+	if (pdev->rx_refill_buf_ring3.hal_srng)
+		htt_srng_setup(soc->htt_handle, idx,
+			       pdev->rx_refill_buf_ring3.hal_srng,
+			       RXDMA_BUF);
+}
+#else
+static inline
+void dp_rxdma_setup_refill_ring3(struct dp_soc *soc,
+				 struct dp_pdev *pdev,
+				 uint8_t idx)
+{ }
+#endif
+
 static QDF_STATUS dp_rxdma_ring_config(struct dp_soc *soc)
 {
 	int i;
@@ -6042,6 +6235,8 @@ static QDF_STATUS dp_rxdma_ring_config(struct dp_soc *soc)
 					       .hal_srng,
 					       RXDMA_BUF);
 
+			dp_rxdma_setup_refill_ring3(soc, pdev, i);
+
 			dp_update_num_mac_rings_for_dbs(soc, &max_mac_rings);
 			dp_err("pdev_id %d max_mac_rings %d",
 			       pdev->pdev_id, max_mac_rings);
@@ -15665,6 +15860,9 @@ static QDF_STATUS dp_pdev_init(struct cdp_soc_t *txrx_soc,
 		dp_soc_reset_intr_mask(soc);
 	}
 
+	/* Reset the cpu ring map if radio is NSS offloaded */
+	dp_soc_reset_ipa_vlan_intr_mask(soc);
+
 	TAILQ_INIT(&pdev->vdev_list);
 	qdf_spinlock_create(&pdev->vdev_list_lock);
 	pdev->vdev_count = 0;

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

@@ -472,6 +472,10 @@ bool dp_tx_multipass_process(struct dp_soc *soc, struct dp_vdev *vdev,
 			     struct dp_tx_msdu_info_s *msdu_info);
 
 void dp_tx_vdev_multipass_deinit(struct dp_vdev *vdev);
+void dp_tx_remove_vlan_tag(struct dp_vdev *vdev, qdf_nbuf_t nbuf);
+void dp_tx_add_groupkey_metadata(struct dp_vdev *vdev,
+				 struct dp_tx_msdu_info_s *msdu_info,
+				 uint16_t group_key);
 #endif
 
 /**

+ 28 - 2
dp/wifi3.0/dp_txrx_wds.c

@@ -610,7 +610,6 @@ int dp_wds_rx_policy_check(uint8_t *rx_tlv_hdr,
  * Return: void
  */
 #ifdef QCA_MULTIPASS_SUPPORT
-static
 void dp_tx_add_groupkey_metadata(struct dp_vdev *vdev,
 		struct dp_tx_msdu_info_s *msdu_info, uint16_t group_key)
 {
@@ -636,7 +635,6 @@ void dp_tx_add_groupkey_metadata(struct dp_vdev *vdev,
  *
  * Return: void
  */
-static
 void dp_tx_remove_vlan_tag(struct dp_vdev *vdev, qdf_nbuf_t nbuf)
 {
 	struct vlan_ethhdr veth_hdr;
@@ -655,6 +653,30 @@ void dp_tx_remove_vlan_tag(struct dp_vdev *vdev, qdf_nbuf_t nbuf)
 	return;
 }
 
+#if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) && \
+	defined(WLAN_MCAST_MLO)
+/**
+ * dp_tx_need_mcast_reinject - If frame needs to be processed in reinject path
+ * @vdev: DP vdev handle
+ *
+ * Return: true if reinject handling is required else false
+ */
+static inline bool
+dp_tx_need_mcast_reinject(struct dp_vdev *vdev)
+{
+	if (vdev->mlo_vdev && vdev->opmode == wlan_op_mode_ap)
+		return true;
+
+	return false;
+}
+#else
+static inline bool
+dp_tx_need_mcast_reinject(struct dp_vdev *vdev)
+{
+	return false;
+}
+
+#endif
 /**
  * dp_tx_need_multipass_process - If frame needs multipass phrase processing
  * @vdev: DP vdev handle
@@ -681,6 +703,10 @@ uint8_t dp_tx_need_multipass_process(struct dp_soc *soc, struct dp_vdev *vdev,
 	*vlan_id = (ntohs(veh->h_vlan_TCI) & VLAN_VID_MASK);
 
 	if (qdf_unlikely(DP_FRAME_IS_MULTICAST((eh)->ether_dhost))) {
+		/* look for handling of multicast packets in reinject path */
+		if (dp_tx_need_mcast_reinject(vdev))
+			return DP_VLAN_UNTAGGED;
+
 		qdf_spin_lock_bh(&vdev->mpass_peer_mutex);
 		TAILQ_FOREACH(txrx_peer, &vdev->mpass_peer_list,
 			      mpass_peer_list_elem) {

+ 28 - 15
dp/wifi3.0/dp_types.h

@@ -1722,6 +1722,21 @@ struct ipa_dp_tx_rsc {
 	void **tx_buf_pool_vaddr_unaligned;
 	qdf_dma_addr_t *tx_buf_pool_paddr_unaligned;
 };
+
+/* IPA uC datapath offload Wlan Rx resources */
+struct ipa_dp_rx_rsc {
+	/* Resource info to be passed to IPA */
+	qdf_dma_addr_t ipa_reo_ring_base_paddr;
+	void *ipa_reo_ring_base_vaddr;
+	uint32_t ipa_reo_ring_size;
+	qdf_dma_addr_t ipa_reo_tp_paddr;
+
+	/* Resource info to be passed to firmware and IPA */
+	qdf_dma_addr_t ipa_rx_refill_buf_ring_base_paddr;
+	void *ipa_rx_refill_buf_ring_base_vaddr;
+	uint32_t ipa_rx_refill_buf_ring_size;
+	qdf_dma_addr_t ipa_rx_refill_buf_hp_paddr;
+};
 #endif
 
 struct dp_tx_msdu_info_s;
@@ -2252,21 +2267,10 @@ struct dp_soc {
 	struct ipa_dp_tx_rsc ipa_uc_tx_rsc_alt;
 #endif
 
-	/* IPA uC datapath offload Wlan Rx resources */
-	struct {
-		/* Resource info to be passed to IPA */
-		qdf_dma_addr_t ipa_reo_ring_base_paddr;
-		void *ipa_reo_ring_base_vaddr;
-		uint32_t ipa_reo_ring_size;
-		qdf_dma_addr_t ipa_reo_tp_paddr;
-
-		/* Resource info to be passed to firmware and IPA */
-		qdf_dma_addr_t ipa_rx_refill_buf_ring_base_paddr;
-		void *ipa_rx_refill_buf_ring_base_vaddr;
-		uint32_t ipa_rx_refill_buf_ring_size;
-		qdf_dma_addr_t ipa_rx_refill_buf_hp_paddr;
-	} ipa_uc_rx_rsc;
-
+	struct ipa_dp_rx_rsc ipa_uc_rx_rsc;
+#ifdef IPA_WDI3_RX_TWO_PIPES
+	struct ipa_dp_rx_rsc ipa_uc_rx_rsc_alt;
+#endif
 	qdf_atomic_t ipa_pipes_enabled;
 	bool ipa_first_tx_db_access;
 	qdf_spinlock_t ipa_rx_buf_map_lock;
@@ -2493,6 +2497,11 @@ struct dp_ipa_resources {
 	qdf_dma_addr_t tx_alt_comp_doorbell_paddr;
 	uint32_t *tx_alt_comp_doorbell_vaddr;
 #endif
+#ifdef IPA_WDI3_RX_TWO_PIPES
+	qdf_shared_mem_t rx_alt_rdy_ring;
+	qdf_shared_mem_t rx_alt_refill_ring;
+	qdf_dma_addr_t rx_alt_ready_doorbell_paddr;
+#endif
 };
 #endif
 
@@ -2795,6 +2804,10 @@ struct dp_pdev {
 
 	/* Second ring used to replenish rx buffers */
 	struct dp_srng rx_refill_buf_ring2;
+#ifdef IPA_WDI3_RX_TWO_PIPES
+	/* Third ring used to replenish rx buffers */
+	struct dp_srng rx_refill_buf_ring3;
+#endif
 
 	/* Empty ring used by firmware to post rx buffers to the MAC */
 	struct dp_srng rx_mac_buf_ring[MAX_RX_MAC_RINGS];

+ 8 - 19
dp/wifi3.0/monitor/dp_rx_mon.c

@@ -83,10 +83,11 @@ dp_rx_mon_populate_cfr_ppdu_info(struct dp_pdev *pdev,
 				 struct cdp_rx_indication_ppdu *cdp_rx_ppdu)
 {
 	struct dp_peer *peer;
-	struct dp_ast_entry *ast_entry;
 	struct dp_soc *soc = pdev->soc;
-	uint32_t ast_index;
 	int chain;
+	uint16_t sw_peer_id;
+	struct mon_rx_user_status *rx_user_status;
+	uint32_t num_users = ppdu_info->com_info.num_users;
 
 	cdp_rx_ppdu->ppdu_id = ppdu_info->com_info.ppdu_id;
 	cdp_rx_ppdu->timestamp = ppdu_info->rx_status.tsft;
@@ -116,23 +117,11 @@ dp_rx_mon_populate_cfr_ppdu_info(struct dp_pdev *pdev,
 				      QDF_MON_STATUS_DCM_SHIFT) & 0x1;
 	}
 
+	qdf_assert_always(num_users <= CDP_MU_MAX_USERS);
 	dp_rx_mon_handle_cfr_mu_info(pdev, ppdu_info, cdp_rx_ppdu);
-	ast_index = ppdu_info->rx_status.ast_index;
-	if (ast_index >= wlan_cfg_get_max_ast_idx(soc->wlan_cfg_ctx)) {
-		cdp_rx_ppdu->peer_id = HTT_INVALID_PEER;
-		cdp_rx_ppdu->num_users = 0;
-		return;
-	}
-
-	ast_entry = soc->ast_table[ast_index];
-	if (!ast_entry || ast_entry->peer_id == HTT_INVALID_PEER) {
-		cdp_rx_ppdu->peer_id = HTT_INVALID_PEER;
-		cdp_rx_ppdu->num_users = 0;
-		return;
-	}
-
-	peer = dp_peer_get_ref_by_id(soc, ast_entry->peer_id,
-				     DP_MOD_ID_RX_PPDU_STATS);
+	rx_user_status = &ppdu_info->rx_user_status[num_users - 1];
+	sw_peer_id = rx_user_status->sw_peer_id;
+	peer = dp_peer_get_ref_by_id(soc, sw_peer_id, DP_MOD_ID_RX_PPDU_STATS);
 	if (!peer) {
 		cdp_rx_ppdu->peer_id = HTT_INVALID_PEER;
 		cdp_rx_ppdu->num_users = 0;
@@ -141,7 +130,7 @@ dp_rx_mon_populate_cfr_ppdu_info(struct dp_pdev *pdev,
 
 	cdp_rx_ppdu->peer_id = peer->peer_id;
 	cdp_rx_ppdu->vdev_id = peer->vdev->vdev_id;
-	cdp_rx_ppdu->num_users = ppdu_info->com_info.num_users;
+	cdp_rx_ppdu->num_users = num_users;
 }
 
 bool

+ 3 - 0
hal/wifi3.0/hal_internal.h

@@ -260,6 +260,9 @@ enum hal_srng_ring_id {
 #ifdef IPA_OFFLOAD
 	HAL_SRNG_WMAC1_SW2RXDMA0_BUF1,
 	HAL_SRNG_WMAC1_SW2RXDMA0_BUF2,
+#ifdef IPA_WDI3_RX_TWO_PIPES
+	HAL_SRNG_WMAC1_SW2RXDMA0_BUF3,
+#endif
 	HAL_SRNG_WMAC1_SW2RXDMA1_BUF,
 #else
 	HAL_SRNG_WMAC1_SW2RXDMA1_BUF,

+ 4 - 0
hal/wifi3.0/qcn9000/hal_9000.c

@@ -2273,7 +2273,11 @@ struct hal_hw_srng_config hw_srng_table_9000[] = {
 	{ /* RXDMA_BUF */
 		.start_ring_id = HAL_SRNG_WMAC1_SW2RXDMA0_BUF0,
 #ifdef IPA_OFFLOAD
+#ifdef IPA_WDI3_RX_TWO_PIPES
+		.max_rings = 4,
+#else
 		.max_rings = 3,
+#endif
 #else
 		.max_rings = 2,
 #endif

+ 7 - 0
ipa/core/inc/wlan_ipa_main.h

@@ -159,6 +159,13 @@ bool ipa_config_is_enabled(void);
  */
 bool ipa_config_is_uc_enabled(void);
 
+/**
+ * ipa_config_is_vlan_enabled() - Is IPA vlan config enabled?
+ *
+ * Return: true if IPA clan support is enabled in IPA config
+ */
+bool ipa_config_is_vlan_enabled(void);
+
 /**
  * ipa_obj_setup() - IPA obj initialization and setup
  * @ipa_ctx: IPA obj context

+ 10 - 0
ipa/core/src/wlan_ipa_main.c

@@ -96,6 +96,14 @@ bool ipa_config_is_uc_enabled(void)
 	return g_ipa_config ? wlan_ipa_uc_is_enabled(g_ipa_config) : 0;
 }
 
+bool ipa_config_is_vlan_enabled(void)
+{
+	if (!ipa_config_is_enabled())
+		return false;
+
+	return g_ipa_config ? g_ipa_config->ipa_vlan_support : 0;
+}
+
 QDF_STATUS ipa_obj_setup(struct wlan_ipa_priv *ipa_ctx)
 {
 	return wlan_ipa_setup(ipa_ctx, g_ipa_config);
@@ -847,6 +855,8 @@ void ipa_component_config_update(struct wlan_objmgr_psoc *psoc)
 		cfg_get(psoc, CFG_DP_IPA_ENABLE_FORCE_VOTING);
 	g_ipa_config->ipa_wds =
 		cfg_get(psoc, CFG_DP_IPA_WDS_STATUS);
+	g_ipa_config->ipa_vlan_support =
+		cfg_get(psoc, CFG_DP_IPA_ENABLE_VLAN_SUPPORT);
 }
 
 void ipa_component_config_free(void)

+ 21 - 1
ipa/dispatcher/inc/cfg_ipa.h

@@ -218,6 +218,25 @@
 		CFG_INI_BOOL("gIPAWds", \
 		false, "Ctrl to enable WDS for EasyMesh")
 
+/*
+ * <ini>
+ * gIPAVlanEnable - IPA vlan support enable
+ * @Default: false
+ *
+ * This ini specifies to enable IPA vlan support
+ *
+ * Related: N/A
+ *
+ * Supported Feature: IPA
+ *
+ * Usage: Internal
+ *
+ * </ini>
+ */
+#define CFG_DP_IPA_ENABLE_VLAN_SUPPORT \
+		CFG_INI_BOOL("gIPAVlanEnable", \
+		false, "Ctrl to enable vlan support with IPA Offload")
+
 #define CFG_IPA \
 	CFG(CFG_DP_IPA_OFFLOAD_CONFIG) \
 	CFG(CFG_DP_IPA_DESC_SIZE) \
@@ -226,5 +245,6 @@
 	CFG(CFG_DP_IPA_LOW_BANDWIDTH_MBPS) \
 	CFG(CFG_DP_IPA_ENABLE_FORCE_VOTING) \
 	CFG(CFG_DP_IPA_UC_TX_BUF_COUNT) \
-	CFG(CFG_DP_IPA_WDS_STATUS)
+	CFG(CFG_DP_IPA_WDS_STATUS) \
+	CFG(CFG_DP_IPA_ENABLE_VLAN_SUPPORT)
 #endif /* _CFG_IPA_H_ */

+ 12 - 0
ipa/dispatcher/inc/wlan_ipa_obj_mgmt_api.h

@@ -79,6 +79,13 @@ static inline bool wlan_ipa_config_is_enabled(void)
  */
 qdf_ipa_wdi_hdl_t wlan_ipa_get_hdl(void *soc, uint8_t pdev_id);
 
+/**
+ * wlan_ipa_is_vlan enabled() - get IPA vlan support enable status
+ *
+ * Return: true - ipa vlan support is enabled
+ *         false - ipa vlan support is not enabled
+ */
+bool wlan_ipa_is_vlan_enabled(void);
 #else
 
 static inline QDF_STATUS ipa_init(void)
@@ -105,6 +112,11 @@ static inline bool wlan_ipa_config_is_enabled(void)
 {
 	return false;
 }
+
+static inline bool wlan_ipa_is_vlan_enabled(void)
+{
+	return false;
+}
 #endif /* IPA_OFFLOAD */
 
 #endif /* _WLAN_IPA_OBJ_MGMT_H_ */

+ 2 - 0
ipa/dispatcher/inc/wlan_ipa_public_struct.h

@@ -41,6 +41,7 @@
  * @ipa_bw_low: IPA bandwidth low threshold
  * @ipa_force_voting: support force bw voting
  * @ipa_wds: WDS support for IPA
+ * @ipa_vlan_support: support got vlan with IPA
  */
 struct wlan_ipa_config {
 	uint32_t ipa_config;
@@ -54,6 +55,7 @@ struct wlan_ipa_config {
 	uint32_t ipa_bw_low;
 	bool ipa_force_voting;
 	bool ipa_wds;
+	bool ipa_vlan_support;
 };
 
 /**

+ 13 - 0
ipa/dispatcher/inc/wlan_ipa_ucfg_api.h

@@ -87,6 +87,14 @@ bool ucfg_ipa_is_enabled(void);
  */
 bool ucfg_ipa_uc_is_enabled(void);
 
+/**
+ * ucfg_ipa_is_vlan enabled() - get IPA vlan support enable status
+ *
+ * Return: true - ipa vlan support is enabled
+ *         false - ipa vlan support is not enabled
+ */
+bool ucfg_ipa_is_vlan_enabled(void);
+
 /**
  * ucfg_ipa_set_dp_handle() - register DP handle
  * @psoc: psoc handle
@@ -495,6 +503,11 @@ static inline bool ucfg_ipa_uc_is_enabled(void)
 	return false;
 }
 
+static inline bool ucfg_ipa_is_vlan_enabled(void)
+{
+	return false;
+}
+
 static inline
 QDF_STATUS ucfg_ipa_set_dp_handle(struct wlan_objmgr_psoc *psoc,
 				     void *dp_soc)

+ 7 - 0
ipa/dispatcher/src/wlan_ipa_obj_mgmt_api.c

@@ -363,3 +363,10 @@ qdf_ipa_wdi_hdl_t wlan_ipa_get_hdl(void *soc, uint8_t pdev_id)
 }
 
 qdf_export_symbol(wlan_ipa_get_hdl);
+
+bool wlan_ipa_is_vlan_enabled(void)
+{
+	return ipa_config_is_vlan_enabled();
+}
+
+qdf_export_symbol(wlan_ipa_is_vlan_enabled);

+ 7 - 0
ipa/dispatcher/src/wlan_ipa_ucfg_api.c

@@ -67,6 +67,13 @@ bool ucfg_ipa_uc_is_enabled(void)
 
 qdf_export_symbol(ucfg_ipa_uc_is_enabled);
 
+bool ucfg_ipa_is_vlan_enabled(void)
+{
+	return ipa_config_is_vlan_enabled();
+}
+
+qdf_export_symbol(ucfg_ipa_is_vlan_enabled);
+
 void ucfg_ipa_set_pdev_id(struct wlan_objmgr_psoc *psoc,
 			  uint8_t pdev_id)
 {

+ 5 - 0
wmi/src/wmi_unified_action_oui_tlv.c

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2016-2018, 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 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
@@ -56,6 +57,10 @@ bool wmi_get_action_oui_id(enum action_oui_id action_id,
 		*id = WMI_VENDOR_OUI_ACTION_DISABLE_FW_TRIGGERED_TWT;
 		return true;
 
+	case ACTION_OUI_EXTEND_WOW_ITO:
+		*id = WMI_VENDOR_OUI_ACTION_EXTEND_WOW_ITO;
+		return true;
+
 	default:
 		return false;
 	}