Merge "qca-wifi: Multi-Link Repeater Functionality"
diff --git a/qca_multi_link/inc/qca_multi_link.h b/qca_multi_link/inc/qca_multi_link.h
new file mode 100644
index 0000000..b997b32
--- /dev/null
+++ b/qca_multi_link/inc/qca_multi_link.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. 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 above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __qca_multi_link_H_
+#define __qca_multi_link_H_
+
+#include <qdf_nbuf.h>
+#include <qdf_module.h>
+#include <qdf_list.h>
+#include <qdf_util.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+#include <br_private.h>
+
+#include <ieee80211.h>
+#include <ieee80211_defines.h>
+#include <qca_multi_link_tbl.h>
+#include "if_upperproto.h"
+
+
+#define QCA_MULTI_LINK_FAST_LANE_LIST_SIZE 6
+#define QCA_MULTI_LINK_NO_BACKHAUL_LIST_SIZE 32
+
+/**
+ * qca_multi_link_needs_enq - rptr return status
+ * @QCA_MULTI_LINK_ALLOW_VAP_ENQ: Allow the packet to be enqueued
+ * @QCA_MULTI_LINK_SKIP_VAP_ENQ: SKIP the enqueue
+ */
+typedef enum qca_multi_link_needs_enq {
+	QCA_MULTI_LINK_ALLOW_VAP_ENQ = 0,
+	QCA_MULTI_LINK_SKIP_VAP_ENQ,
+} qca_multi_link_needs_enq_t;
+
+/**
+ * enum qca_multi_link_status - rptr return status
+ * @qca_multi_link_PKT_ALLOW: Allow the packet to be received or transmitted
+ * @qca_multi_link_PKT_DROP: Drop the packet
+ * @qca_multi_link_PKT_CONSUMED: The packet is consumed in the repeater processing
+ */
+typedef enum qca_multi_link_status {
+	QCA_MULTI_LINK_PKT_NONE = 0,
+	QCA_MULTI_LINK_PKT_ALLOW,
+	QCA_MULTI_LINK_PKT_DROP,
+	QCA_MULTI_LINK_PKT_CONSUMED
+} qca_multi_link_status_t;
+
+/**
+ * struct qca_multi_link_list_node - rptr list node
+ * @list: linked list node
+ * @wiphy: wiphy pointer
+ */
+struct qca_multi_link_list_node {
+	qdf_list_node_t node;
+	struct wiphy *wiphy;
+};
+
+typedef struct qca_multi_link_parameters {
+	bool rptr_processing_enable;
+	bool loop_detected;
+	bool always_primary;
+	bool force_client_mcast_traffic;
+	bool drop_secondary_mcast;
+	struct wiphy *primary_wiphy;
+	uint8_t total_stavaps_up;
+	qdf_list_t no_backhaul_list;
+	qdf_list_t fast_lane_list;
+} qca_multi_link_parameters_t;
+
+void qca_multi_link_init_module(void);
+void qca_multi_link_deinit_module(void);
+uint8_t qca_multi_link_get_num_sta(void);
+void qca_multi_link_append_num_sta(bool inc_or_dec);
+bool qca_multi_link_is_dbdc_processing_reqd(struct net_device *net_dev);
+void qca_multi_link_set_drop_sec_mcast(bool val);
+void qca_multi_link_set_force_client_mcast(bool val);
+void qca_multi_link_set_always_primary(bool val);
+void qca_multi_link_set_dbdc_enable(bool val);
+struct wiphy *qca_multi_link_get_primary_radio(void);
+void qca_multi_link_set_primary_radio(struct wiphy *primary_wiphy);
+bool qca_multi_link_add_fastlane_radio(struct wiphy *fl_wiphy);
+bool qca_multi_link_remove_fastlane_radio(struct wiphy *fl_wiphy);
+bool qca_multi_link_add_no_backhaul_radio(struct wiphy *no_bl_wiphy);
+bool qca_multi_link_remove_no_backhaul_radio(struct wiphy *no_bl_wiphy);
+bool qca_multi_link_ap_rx(struct net_device *net_dev, qdf_nbuf_t nbuf);
+bool qca_multi_link_sta_rx(struct net_device *net_dev, qdf_nbuf_t nbuf,
+			  qca_multi_link_needs_enq_t allow_vap_enq);
+bool qca_multi_link_sta_tx(struct net_device *net_dev, qdf_nbuf_t nbuf);
+#endif
diff --git a/qca_multi_link/inc/qca_multi_link_tbl.h b/qca_multi_link/inc/qca_multi_link_tbl.h
new file mode 100644
index 0000000..4893ec2
--- /dev/null
+++ b/qca_multi_link/inc/qca_multi_link_tbl.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. 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 above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: qca_multi_link_tbl (QAL Bridge)
+ * QCA driver framework for OS notifier handlers
+ */
+
+#ifndef __qca_multi_link_tbl_H
+#define __qca_multi_link_tbl_H
+
+#include "qdf_types.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+#include <br_private.h>
+
+typedef struct qca_multi_link_tbl_entry {
+	struct wireless_dev *qal_fdb_ieee80211_ptr;
+	struct net_device *qal_fdb_dev;
+	uint8_t qal_fdb_is_local;
+	unsigned char qal_mac_addr[6];
+} qca_multi_link_tbl_entry_t;
+
+/**
+ *
+ * qca_multi_link_tbl_get_eth_entries() - Get ethernet bridge entries
+ *
+ * To be called from the code with a valid netdevice
+ *
+ * Return: Number of entries copied
+ */
+int qca_multi_link_tbl_get_eth_entries(struct net_device *net_dev,
+				      void *fill_buff, int buff_size);
+
+/**
+ *
+ * qca_multi_link_tbl_find_sta_or_ap() - Get the AP or Station
+ *					from bridge on the same radio
+ *
+ * To be called from the code with a valid netdevice
+ *
+ * Return: struct net_device
+ */
+struct net_device *qca_multi_link_tbl_find_sta_or_ap(struct net_device *net_dev, uint8_t dev_type);
+
+/**
+ *
+ * qca_multi_link_tbl_delete_entry() - Delete a non-local bridge fdb entry
+ *
+ * To be called from the code with a valid netdevice
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS qca_multi_link_tbl_delete_entry(struct net_device *net_dev, uint8_t *addr);
+
+/**
+ *
+ * qca_multi_link_tbl_has_entry() - check if there is fdb entry for the mac-address
+ *
+ * To be called from the code with a valid qal_entry pointer
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS qca_multi_link_tbl_has_entry(struct net_device *net_dev,
+				       const char *addr, uint16_t vlan_id,
+				       qca_multi_link_tbl_entry_t *qca_ml_entry);
+/**
+ *
+ * qca_multi_link_tbl_register_update_notifier() - register to br_fdb update notifier chain
+ *
+ * To be called from each callback with its own handle
+ *
+ * Return: None
+ */
+QDF_STATUS qca_multi_link_tbl_register_update_notifier(void *nb);
+
+/**
+ * qca_multi_link_tbl_unregister_update_notifier() - unregister linux br_fdb update notifier chain
+ *
+ * To be called from each callback with its own handle
+ *
+ * Return: None
+ */
+QDF_STATUS qca_multi_link_tbl_unregister_update_notifier(void *nb);
+
+/**
+ *
+ * qca_multi_link_tbl_register_notifier() - register to br_fdb notifier chain
+ *
+ * To be called from each callback with its own handle
+ *
+ * Return: None
+ */
+QDF_STATUS qca_multi_link_tbl_register_notifier(void *nb);
+
+/**
+ * qca_multi_link_tbl_unregister_notifier() - unregister linux br_fdb notifier chain
+ *
+ * To be called from each callback with its own handle
+ *
+ * Return: None
+ */
+QDF_STATUS qca_multi_link_tbl_unregister_notifier(void *nb);
+
+/**
+ *
+ * qca_multi_link_tbl_get_bridge_dev() - Get bridge netdevice from port
+ *
+ * To be called from the code with a valid port netdevice
+ *
+ * Return: Bridge netdevice
+ */
+struct net_device *qca_multi_link_tbl_get_bridge_dev(struct net_device *port_dev);
+#endif /* __qca_multi_link_tbl_H */
diff --git a/qca_multi_link/src/qca_multi_link.c b/qca_multi_link/src/qca_multi_link.c
new file mode 100644
index 0000000..003d58a
--- /dev/null
+++ b/qca_multi_link/src/qca_multi_link.c
@@ -0,0 +1,1219 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. 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 above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "qca_multi_link.h"
+
+static bool is_initialized;
+qca_multi_link_parameters_t qca_multi_link_cfg;
+
+static inline bool is_fast_lane_radio(struct wiphy *fl_wiphy)
+{
+	qdf_list_node_t *node = NULL, *next_node = NULL;
+
+	if (!fl_wiphy) {
+		return false;
+	}
+
+	if (qdf_list_empty(&qca_multi_link_cfg.fast_lane_list)) {
+		return false;
+	}
+
+	qdf_list_peek_front(&qca_multi_link_cfg.fast_lane_list,
+		(qdf_list_node_t **)&next_node);
+	while (next_node) {
+		struct qca_multi_link_list_node *fast_lane_node
+		= (struct qca_multi_link_list_node *)next_node;
+		if (fast_lane_node->wiphy == fl_wiphy) {
+			return true;
+		} else {
+			node = next_node;
+			next_node = NULL;
+		if ((qdf_list_peek_next(&qca_multi_link_cfg.fast_lane_list, node, &next_node))
+			!= QDF_STATUS_SUCCESS) {
+			return false;
+		}
+		}
+	}
+	return false;
+}
+
+static inline bool is_no_backhaul_radio(struct wiphy *no_bl_wiphy)
+{
+	qdf_list_node_t *node = NULL, *next_node = NULL;
+
+	if (!no_bl_wiphy) {
+		return false;
+	}
+
+	if (qdf_list_empty(&qca_multi_link_cfg.no_backhaul_list)) {
+		return false;
+	}
+
+	qdf_list_peek_front(&qca_multi_link_cfg.no_backhaul_list,
+		(qdf_list_node_t **)&next_node);
+	while (next_node) {
+		struct qca_multi_link_list_node *no_bl_node
+		= (struct qca_multi_link_list_node *)next_node;
+		if (no_bl_node->wiphy == no_bl_wiphy) {
+			return true;
+		} else {
+			node = next_node;
+			next_node = NULL;
+			if ((qdf_list_peek_next(&qca_multi_link_cfg.no_backhaul_list, node, &next_node))
+				!= QDF_STATUS_SUCCESS) {
+				return false;
+			}
+		}
+	}
+	return false;
+}
+
+/**
+ * qca_multi_link_is_primary_radio() - Check if this is a primary radio
+ *
+ * Return: true: if it primary radio
+ *	   false: if it is secondary radio
+ */
+static inline bool qca_multi_link_is_primary_radio(struct wiphy *dev_wiphy)
+{
+	bool is_primary = false;
+
+	if (!qca_multi_link_cfg.primary_wiphy || !dev_wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("\nprimary_wiphy is NULL\n"));
+		is_primary = false;
+	} else {
+		is_primary = (dev_wiphy == qca_multi_link_cfg.primary_wiphy)?true:false;
+	}
+	return is_primary;
+}
+
+/**
+ * qca_multi_link_need_procesing() - Check if repeater processing is required
+ *
+ * Return: true: processing is required
+ *	   false: processing is not required
+ */
+static inline bool qca_multi_link_need_procesing(void)
+{
+	if ((!qca_multi_link_cfg.rptr_processing_enable)
+		|| (qca_multi_link_cfg.total_stavaps_up < 2)) {
+		return false;
+	}
+	return true;
+}
+
+/**
+ * qca_multi_link_pktfrom_ownsrc() - Check if packet is from same device
+ *
+ * Return: true: packet is from same device
+ *	   false:  packet is not from same device
+ */
+static inline bool qca_multi_link_pktfrom_ownsrc(struct net_device *net_dev, qdf_nbuf_t nbuf)
+{
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	if (qdf_is_macaddr_equal((struct qdf_mac_addr *)net_dev->dev_addr,
+		(struct qdf_mac_addr *)eh->ether_shost)) {
+		return true;
+	}
+	return false;
+}
+
+/**
+ * qca_multi_link_drop_secondary_mcast() - Check if mcast to be dropped on secondary
+ *
+ * Return: true: Drop the packet
+ *	   false: Do not drop
+ */
+static inline bool qca_multi_link_drop_secondary_mcast(qdf_nbuf_t nbuf)
+{
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+	uint8_t is_mcast = IEEE80211_IS_MULTICAST(eh->ether_dhost);
+
+	if (is_mcast && qca_multi_link_cfg.drop_secondary_mcast) {
+		return true;
+	}
+	return false;
+}
+
+/**
+ * qca_multi_link_drop_always_primary() - Check if packet to be dropped for always_primary
+ *
+ * Return: true: Drop the packet
+ *	   false: Do not drop
+ */
+static inline bool qca_multi_link_drop_always_primary(bool is_primary, qdf_nbuf_t nbuf)
+{
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	if (qca_multi_link_cfg.always_primary) {
+		if (is_primary) {
+			return false;
+		} else {
+			if (eh->ether_type != qdf_htons(ETHERTYPE_PAE)) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+/**
+ * qca_multi_link_deinit_module() - De-initialize the repeater base structute
+ * Return: void
+ */
+void qca_multi_link_deinit_module(void)
+{
+	if (!is_initialized)
+		return;
+
+	qca_multi_link_cfg.total_stavaps_up = 0;
+	qca_multi_link_cfg.loop_detected = 0;
+	qca_multi_link_cfg.primary_wiphy = NULL;
+	qdf_list_destroy(&qca_multi_link_cfg.fast_lane_list);
+	qdf_list_destroy(&qca_multi_link_cfg.no_backhaul_list);
+	is_initialized = false;
+
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_INFO,
+		FL("\n******QCA RPtr De-Init Done***********\n"));
+}
+
+qdf_export_symbol(qca_multi_link_deinit_module);
+
+/**
+ * qca_multi_link_init_module() - Initialize the repeater base structute
+ *
+ * Return: void
+ */
+void qca_multi_link_init_module(void)
+{
+	if (is_initialized)
+		return;
+
+	is_initialized = true;
+	qca_multi_link_cfg.total_stavaps_up = 0;
+	qca_multi_link_cfg.loop_detected = 0;
+	qca_multi_link_cfg.primary_wiphy = NULL;
+	qdf_list_create(&qca_multi_link_cfg.fast_lane_list, QCA_MULTI_LINK_FAST_LANE_LIST_SIZE);
+	qdf_list_create(&qca_multi_link_cfg.no_backhaul_list, QCA_MULTI_LINK_NO_BACKHAUL_LIST_SIZE);
+
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_INFO,
+		FL("\n******QCA Repeater Initialization Done***********\n"));
+}
+
+qdf_export_symbol(qca_multi_link_init_module);
+
+/**
+ * qca_multi_link_get_num_sta() - Get the total number of sta vaps up.
+ *
+ * Return: int
+ */
+uint8_t qca_multi_link_get_num_sta(void)
+{
+	return qca_multi_link_cfg.total_stavaps_up;
+}
+
+qdf_export_symbol(qca_multi_link_get_num_sta);
+
+/**
+ * qca_multi_link_append_num_sta() - Append the total number of sta vaps up.
+ * @inc_or_dec: true to increment and false to decrement
+ *
+ * Return: void
+ */
+void qca_multi_link_append_num_sta(bool inc_or_dec)
+{
+	if (inc_or_dec) {
+		qca_multi_link_cfg.total_stavaps_up++;
+		if (qca_multi_link_cfg.total_stavaps_up > 1) {
+			qca_multi_link_set_drop_sec_mcast(true);
+		}
+	} else {
+		if (qca_multi_link_cfg.total_stavaps_up == 0) {
+			return;
+		}
+
+		qca_multi_link_cfg.total_stavaps_up--;
+		if (qca_multi_link_cfg.total_stavaps_up <= 1) {
+			qca_multi_link_cfg.loop_detected = 0;
+		}
+	}
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_NONE,
+		FL("\nStation vap number in Repeater is val=%d***********\n"),
+		qca_multi_link_cfg.total_stavaps_up);
+}
+
+qdf_export_symbol(qca_multi_link_append_num_sta);
+
+/**
+ * qca_multi_link_is_dbdc_processing_reqd() - Check if dbdc processing is required
+ * @net_dev: current net device
+ *
+ * Return: true: loop is detected
+ *	   false: loop not detected
+ */
+bool qca_multi_link_is_dbdc_processing_reqd(struct net_device *net_dev)
+{
+	if (qca_multi_link_cfg.total_stavaps_up > 2)
+		return (qca_multi_link_cfg.loop_detected && qca_multi_link_cfg.rptr_processing_enable);
+	else
+		return false;
+}
+
+qdf_export_symbol(qca_multi_link_is_dbdc_processing_reqd);
+
+/**
+ * qca_multi_link_set_drop_sec_mcast() - set the drop secondary mcast flag
+ * @val: boolean true or false
+ *
+ */
+void qca_multi_link_set_drop_sec_mcast(bool val)
+{
+	qca_multi_link_cfg.drop_secondary_mcast = val;
+}
+
+qdf_export_symbol(qca_multi_link_set_drop_sec_mcast);
+
+/**
+ * qca_multi_link_set_force_client_mcast() - set the flag to force client mcast traffic
+ * @val: boolean true or false
+ *
+ */
+void qca_multi_link_set_force_client_mcast(bool val)
+{
+	qca_multi_link_cfg.force_client_mcast_traffic = val;
+}
+
+qdf_export_symbol(qca_multi_link_set_force_client_mcast);
+
+/**
+ * qca_multi_link_set_always_primary() - set the flag for always primary flag
+ * @val: boolean true or false
+ *
+ */
+void qca_multi_link_set_always_primary(bool val)
+{
+	qca_multi_link_cfg.always_primary = val;
+}
+
+qdf_export_symbol(qca_multi_link_set_always_primary);
+
+/**
+ * qca_multi_link_set_dbdc_enable() - set the dbdc enable flag
+ * @val: boolean true or false
+ */
+void qca_multi_link_set_dbdc_enable(bool val)
+{
+	qca_multi_link_cfg.rptr_processing_enable = val;
+	if (!val) {
+		qca_multi_link_cfg.loop_detected = 0;
+	}
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+		FL("\nSetting DBDC enable = val%d\n"), val);
+}
+
+qdf_export_symbol(qca_multi_link_set_dbdc_enable);
+
+/**
+ * qca_multi_link_get_primary_radio() - set the dbdc enable flag
+ * @primary_wiphy: wiphy pointer of primary radio device
+ */
+struct wiphy *qca_multi_link_get_primary_radio(void)
+{
+	return qca_multi_link_cfg.primary_wiphy;
+}
+
+qdf_export_symbol(qca_multi_link_get_primary_radio);
+
+/**
+ * qca_multi_link_set_primary_radio() - set the primary radio
+ * @primary_wiphy: wiphy pointer of primary radio device
+ */
+void qca_multi_link_set_primary_radio(struct wiphy *primary_wiphy)
+{
+	if (!primary_wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("\nNull wiphy in Setting primary radio\n"));
+		return;
+	}
+	qca_multi_link_cfg.primary_wiphy = primary_wiphy;
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_INFO,
+		FL("\nSetting primary radio for wiphy%p\n"), primary_wiphy);
+}
+
+qdf_export_symbol(qca_multi_link_set_primary_radio);
+
+/**
+ * qca_multi_link_add_fastlane_radio() - add the fast lane radio pointer to list
+ * @primary_wiphy: wiphy pointer of fast-lane radio device
+ *
+ * Return: false: addition not successful
+ *	   true: addition is successful
+ */
+bool qca_multi_link_add_fastlane_radio(struct wiphy *fl_wiphy)
+{
+	struct qca_multi_link_list_node *fast_lane_node;
+
+	if (!fl_wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_WARN,
+			FL(" Fast lane radio could not be set - wiphy is NULL\n"));
+		return false;
+	}
+
+	fast_lane_node = qdf_mem_malloc(sizeof(struct qca_multi_link_list_node));
+	if (!fast_lane_node) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("Could not allocate fast-lane node for wiphy%p\n"), fl_wiphy);
+		return false;
+	}
+
+	fast_lane_node->wiphy = fl_wiphy;
+	if (qdf_list_insert_front(&qca_multi_link_cfg.fast_lane_list, &fast_lane_node->node)
+		== QDF_STATUS_SUCCESS) {
+		return true;
+	}
+	return false;
+}
+
+qdf_export_symbol(qca_multi_link_add_fastlane_radio);
+
+/**
+ * qca_multi_link_remove_fastlane_radio() - remove the fast lane radio pointer from list
+ * @primary_wiphy: wiphy pointer of fast-lane radio device
+ *
+ * Return: false: addition not successful
+ *	   true: addition is successful
+ */
+bool qca_multi_link_remove_fastlane_radio(struct wiphy *fl_wiphy)
+{
+	qdf_list_node_t *node = NULL, *next_node = NULL;
+
+	if (!fl_wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_WARN,
+			FL(" Fast lane radio could not be removed - wiphy is NULL\n"));
+		return false;
+	}
+
+	qdf_list_peek_front(&qca_multi_link_cfg.fast_lane_list,
+		(qdf_list_node_t **)&next_node);
+	while (next_node) {
+		struct qca_multi_link_list_node *fast_lane_node
+			= (struct qca_multi_link_list_node *)next_node;
+		if (fast_lane_node->wiphy == fl_wiphy) {
+			qdf_list_remove_node(&qca_multi_link_cfg.fast_lane_list, next_node);
+			qdf_mem_free(fast_lane_node);
+			return true;
+		} else {
+			node = next_node;
+			next_node = NULL;
+			if ((qdf_list_peek_next(&qca_multi_link_cfg.fast_lane_list, node,
+				(qdf_list_node_t **)&next_node)) != QDF_STATUS_SUCCESS) {
+				return false;
+			}
+		}
+	}
+	return false;
+}
+
+qdf_export_symbol(qca_multi_link_remove_fastlane_radio);
+
+/**
+ * qca_multi_link_add_no_backhaul_radio() - add the no backhaul radio pointer to list
+ * @primary_wiphy: wiphy pointer of fast-lane radio device
+ *
+ * Return: false: addition not successful
+ *	   true: addition is successful
+ */
+bool qca_multi_link_add_no_backhaul_radio(struct wiphy *no_bl_wiphy)
+{
+	struct qca_multi_link_list_node *no_bl_node;
+
+	if (!no_bl_wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_WARN,
+			FL(" No backhaul radio could not be set - wiphy is NULL\n"));
+		return false;
+	}
+
+	no_bl_node = qdf_mem_malloc(sizeof(struct qca_multi_link_list_node));
+	if (!no_bl_node) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("Could not allocate back-haul node for wiphy%p\n"), no_bl_node);
+		return false;
+	}
+
+	no_bl_node->wiphy = no_bl_wiphy;
+	if (qdf_list_insert_front(&qca_multi_link_cfg.no_backhaul_list, &no_bl_node->node)
+		== QDF_STATUS_SUCCESS) {
+		return true;
+	}
+	return false;
+}
+
+qdf_export_symbol(qca_multi_link_add_no_backhaul_radio);
+
+/**
+ * qca_multi_link_remove_no_backhaul_radio() - remove no-backhaul radio pointer from list
+ * @primary_wiphy: wiphy pointer of no-backhaul radio device
+ *
+ * Return: false: addition not successful
+ *	   true: addition is successful
+ */
+bool qca_multi_link_remove_no_backhaul_radio(struct wiphy *no_bl_wiphy)
+{
+	qdf_list_node_t *node = NULL, *next_node = NULL;
+
+	if (!no_bl_wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_WARN,
+			FL(" No backhaul radio could not be removed - wiphy is NULL\n"));
+		return false;
+	}
+
+	qdf_list_peek_front(&qca_multi_link_cfg.no_backhaul_list,
+		(qdf_list_node_t **)&next_node);
+	while (next_node) {
+		struct qca_multi_link_list_node *no_bl_node
+			= (struct qca_multi_link_list_node *)next_node;
+		if (no_bl_node->wiphy == no_bl_wiphy) {
+			qdf_list_remove_node(&qca_multi_link_cfg.no_backhaul_list, next_node);
+			qdf_mem_free(no_bl_node);
+			return true;
+		} else {
+			node = next_node;
+			next_node = NULL;
+			if ((qdf_list_peek_next(&qca_multi_link_cfg.no_backhaul_list, node, &next_node))
+				!= QDF_STATUS_SUCCESS) {
+				return false;
+			}
+		}
+	}
+	return false;
+}
+
+qdf_export_symbol(qca_multi_link_remove_no_backhaul_radio);
+
+/**
+ * qca_multi_link_secondary_ap_rx() - Processing for frames recieved on Secondary AP VAP
+ * @net_device: net device handle
+ * @nbuf: frame
+ *
+ * Return: qca_multi_link_status_t
+ *	   QCA_MULTI_LINK_PKT_ALLOW: frame should be processed further by caller.
+ *	   QCA_MULTI_LINK_PKT_DROP: frame to be dropped.
+ *	   QCA_MULTI_LINK_PKT_CONSUMED: frame is consumed.
+ */
+static qca_multi_link_status_t qca_multi_link_secondary_ap_rx(struct net_device *ap_dev, qdf_nbuf_t nbuf)
+{
+	struct wiphy *ap_wiphy = NULL;
+	struct net_device *sta_dev = NULL;
+	qca_multi_link_tbl_entry_t qca_ml_entry;
+
+	QDF_STATUS qal_status = QDF_STATUS_E_FAILURE;
+	bool enqueue_to_sta_vap = false;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	ap_wiphy = ap_dev->ieee80211_ptr->wiphy;
+
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("Secondary AP Rx: always_primary=%d, loop_detected=%d,\
+				drop_secondary_mcast=%d, shost %pM dhost %pM\n"),
+				qca_multi_link_cfg.always_primary, qca_multi_link_cfg.loop_detected,
+				qca_multi_link_cfg.drop_secondary_mcast, eh->ether_shost, eh->ether_dhost);
+
+	/*
+	 *If the AP is on a fast lane radio, always give the packet to bridge.
+	 */
+	if (is_fast_lane_radio(ap_wiphy)) {
+		return QCA_MULTI_LINK_PKT_ALLOW;
+	}
+
+	qca_ml_entry.qal_fdb_ieee80211_ptr = NULL;
+	qca_ml_entry.qal_fdb_dev = NULL;
+	qca_ml_entry.qal_fdb_is_local = 0;
+	qal_status = qca_multi_link_tbl_has_entry(ap_dev, eh->ether_dhost, 0,
+				&qca_ml_entry);
+	if (qal_status == QDF_STATUS_SUCCESS) {
+
+		/*
+		 * Check the FDB entry type, if the mac-address is learnt on a port which is
+		 * of type station, then it is a source on the RootAP side and enqueue the
+		 * packet to the corresponding station vap. Else give the packet to bridge.
+		 */
+		if (qca_ml_entry.qal_fdb_ieee80211_ptr
+		&& (qca_ml_entry.qal_fdb_ieee80211_ptr->iftype == NL80211_IFTYPE_STATION)) {
+			enqueue_to_sta_vap = true;
+		}
+	} else {
+
+		/*
+		 * If there is no fdb entry, then also the destination might be on the RootAP side,
+		 * enqueue the packet to the corresponding station vap.
+		 */
+		enqueue_to_sta_vap = true;
+	}
+
+	if (enqueue_to_sta_vap) {
+
+		/*
+		 * Find the station vap corresponding to the AP vap.
+		 */
+		sta_dev = qca_multi_link_tbl_find_sta_or_ap(ap_dev, 1);
+		if (!sta_dev) {
+			QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+				FL("Null STA device found %pM - Give to bridge\n"), eh->ether_shost);
+			return QCA_MULTI_LINK_PKT_DROP;
+		}
+
+		dev_hold(sta_dev);
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("shost %pM dhost %pM \n"), eh->ether_shost, eh->ether_dhost);
+
+		/*
+		 * For packets destined to sources on RootAP directly enq to STA vap.
+		 */
+		sta_dev->netdev_ops->ndo_start_xmit(nbuf, sta_dev);
+		dev_put(sta_dev);
+		return QCA_MULTI_LINK_PKT_CONSUMED;
+	}
+
+	return QCA_MULTI_LINK_PKT_ALLOW;
+}
+
+/**
+ * qca_multi_link_ap_rx() - Processing for frames recieved on AP VAP
+ * @net_device: net device handle
+ * @nbuf: frame
+ *
+ * Return: false: frame not consumed and should be processed further by caller
+ *	   true: frame consumed
+ */
+bool qca_multi_link_ap_rx(struct net_device *net_dev, qdf_nbuf_t nbuf)
+{
+	uint8_t is_mcast;
+	uint8_t is_eapol;
+	struct net_device *ap_dev = net_dev;
+	qca_multi_link_status_t status = QCA_MULTI_LINK_PKT_NONE;
+	struct wiphy *ap_wiphy = NULL;
+	bool drop_packet = false;
+	bool is_primary = false;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	if (!qca_multi_link_need_procesing()) {
+		goto end;
+	}
+
+	if (!qca_multi_link_cfg.loop_detected) {
+		goto end;
+	}
+
+	/*
+	 * If it is mcast/broadcast frame, AST search cannot be done, so give
+	 * the frame up the stack
+	 * If it is EAPOL frame, just give the frame up the stack
+	 */
+	is_mcast = IEEE80211_IS_MULTICAST(eh->ether_dhost);
+	is_eapol = (eh->ether_type == htons(ETHERTYPE_PAE));
+	if (is_mcast || is_eapol) {
+		goto end;
+	}
+
+	ap_wiphy = ap_dev->ieee80211_ptr->wiphy;
+	is_primary = qca_multi_link_is_primary_radio(ap_wiphy);
+
+	if (is_primary) {
+		goto end;
+	} else {
+		dev_hold(ap_dev);
+		status = qca_multi_link_secondary_ap_rx(ap_dev, nbuf);
+		dev_put(ap_dev);
+	}
+
+	if (status == QCA_MULTI_LINK_PKT_ALLOW) {
+		goto end;
+	} else if (status == QCA_MULTI_LINK_PKT_CONSUMED) {
+		return true;
+	} else if (status == QCA_MULTI_LINK_PKT_DROP) {
+		drop_packet = true;
+	}
+
+end:
+	if (drop_packet) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("\n STA TX - Drop Packet for Mac=%pM\n"), eh->ether_shost);
+		qdf_nbuf_free(nbuf);
+		return true;
+	}
+	return false;
+}
+
+qdf_export_symbol(qca_multi_link_ap_rx);
+
+/**
+ * qca_multi_link_secondary_sta_rx() - Processing for frames recieved on secondary station vap
+ * @net_dev: station net device
+ * @nbuf: frame
+ *
+ * Return: @qca_multi_link_status_t
+ *	   QCA_MULTI_LINK_PKT_ALLOW: frame should be processed further by caller
+ *	   QCA_MULTI_LINK_PKT_DROP: frame to be dropped.
+ *	   QCA_MULTI_LINK_PKT_CONSUMED: frame is consumed.
+ */
+static qca_multi_link_status_t qca_multi_link_secondary_sta_rx(struct net_device *net_dev,
+							      qdf_nbuf_t nbuf,
+							      qca_multi_link_needs_enq_t allow_vap_enq)
+{
+	uint8_t is_mcast;
+	qca_multi_link_tbl_entry_t qca_ml_entry;
+	struct wiphy *sta_wiphy = NULL;
+	struct net_device *sta_dev = net_dev;
+	struct net_device *ap_dev = NULL;
+	QDF_STATUS qal_status = QDF_STATUS_E_FAILURE;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	sta_wiphy = sta_dev->ieee80211_ptr->wiphy;
+	is_mcast = IEEE80211_IS_MULTICAST(eh->ether_dhost);
+
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("Secondary STA Rx:always_primary=%d, loop_detected=%d,\
+			drop_secondary_mcast=%d, shost %pM dhost %pM is_mcast=%d\n"),
+			qca_multi_link_cfg.always_primary, qca_multi_link_cfg.loop_detected,
+			qca_multi_link_cfg.drop_secondary_mcast, eh->ether_shost, eh->ether_dhost, is_mcast);
+
+	/*
+	 * Mcast packets handling.
+	 */
+	if (is_mcast) {
+
+		/*
+		 * Always drop mcast packets on secondary radio when loop has been detected.
+		 */
+		if (qca_multi_link_cfg.loop_detected) {
+			return QCA_MULTI_LINK_PKT_DROP;
+		}
+
+		qca_ml_entry.qal_fdb_ieee80211_ptr = NULL;
+		qca_ml_entry.qal_fdb_dev = NULL;
+		qca_ml_entry.qal_fdb_is_local = 0;
+		qal_status = qca_multi_link_tbl_has_entry(sta_dev, eh->ether_shost, 0,
+							&qca_ml_entry);
+		if (qal_status != QDF_STATUS_SUCCESS) {
+			if (!qca_multi_link_cfg.loop_detected
+				&& !qca_multi_link_cfg.drop_secondary_mcast) {
+		/*
+		 * This condition is to allow packets on Secondary Station
+		 * when stations are connected to different RootAPs and loop is not
+		 * detected.
+		 */
+				return QCA_MULTI_LINK_PKT_ALLOW;
+			} else {
+				return QCA_MULTI_LINK_PKT_DROP;
+			}
+		}
+
+		/*
+		 * Case 1:
+		 * ieee80211_ptr pointer being NULL indicates that the port
+		 * corresponding to the fdb entry is a non-wireless/ethernet
+		 * device behind the repeater and the packet is a mcast looped packet.
+		 * Case 2:
+		 * ieee80211_ptr pointer being non NULL indicates that the source
+		 * corresponding to the fdb entry is a wireless device
+		 * behind the repeater and the packet is a mcast looped packet.
+		 */
+
+		if (qca_ml_entry.qal_fdb_ieee80211_ptr && (qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy != sta_wiphy)) {
+			if (!qca_multi_link_cfg.loop_detected) {
+				if (qca_ml_entry.qal_fdb_is_local
+					&& (qca_ml_entry.qal_fdb_ieee80211_ptr->iftype == NL80211_IFTYPE_STATION)) {
+					qca_multi_link_cfg.loop_detected = true;
+					QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_INFO, FL("\n****Wifi Rptr Loop Detected****\n"));
+				}
+			}
+			return QCA_MULTI_LINK_PKT_DROP;
+		}
+
+		if (qca_multi_link_drop_secondary_mcast(nbuf)) {
+			return QCA_MULTI_LINK_PKT_DROP;
+		}
+
+		/*
+		 * If the mac-address is learnt on the station in the bridge,
+		 * then the mcast packet is from a source on the RootAP side and we
+		 * should allow the packet.
+		 * This check on secondary will take of the case where stations are connected to different RootAPs
+		 * and loop is not detected.
+		 */
+		if (qca_ml_entry.qal_fdb_dev == sta_dev) {
+			return QCA_MULTI_LINK_PKT_ALLOW;
+		}
+
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	/*
+	 * Unicast packets handling received on secondary Stations.
+	 */
+	if (qca_multi_link_cfg.loop_detected) {
+
+		if (allow_vap_enq == QCA_MULTI_LINK_SKIP_VAP_ENQ) {
+			return QCA_MULTI_LINK_PKT_ALLOW;
+		}
+
+		qca_ml_entry.qal_fdb_ieee80211_ptr = NULL;
+		qca_ml_entry.qal_fdb_dev = NULL;
+		qca_ml_entry.qal_fdb_is_local = 0;
+		qal_status = qca_multi_link_tbl_has_entry(sta_dev, eh->ether_dhost, 0,
+							&qca_ml_entry);
+		if (qal_status == QDF_STATUS_SUCCESS) {
+
+			/*
+			 * Unicast packets destined to ethernets or bridge should never come
+			 * on secondary stations.
+			 */
+			if (!qca_ml_entry.qal_fdb_ieee80211_ptr) {
+				return QCA_MULTI_LINK_PKT_DROP;
+			}
+
+			/*
+			 * Compare the physical device and check if the destination is a client
+			 * on the same radio, then enqueue directly to AP vap.
+			 */
+			if ((qca_ml_entry.qal_fdb_ieee80211_ptr->iftype == NL80211_IFTYPE_AP)
+				&& (qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy == sta_wiphy)) {
+				QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("Unicast Sec STA to AP direct enq for\
+					shost %pM dhost %pM \n"), eh->ether_shost, eh->ether_dhost);
+			/*
+			 * Holding the AP dev so that it cannot be brought down
+			 * while we are enqueueing.
+			 */
+			dev_hold(qca_ml_entry.qal_fdb_dev);
+			qca_ml_entry.qal_fdb_dev->netdev_ops->ndo_start_xmit(nbuf, qca_ml_entry.qal_fdb_dev);
+			dev_put(qca_ml_entry.qal_fdb_dev);
+			return QCA_MULTI_LINK_PKT_CONSUMED;
+			}
+
+			return QCA_MULTI_LINK_PKT_DROP;
+		} else {
+
+			/*
+			 * If there is no bridge fdb entry for unicast packets received on secondary
+			 * station, give the packet to the first found AP vap entry in the bridge table.
+			 */
+			QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("No Fdb entry on sec radio\
+				for ucast pkt with dhost %pM \n"), eh->ether_dhost);
+			/*
+			 * Find the AP vap corresponding to the station vap.
+			 */
+			ap_dev = qca_multi_link_tbl_find_sta_or_ap(sta_dev, 0);
+			if (!ap_dev) {
+				QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+				FL("Null AP device found %pM - Give to bridge\n"), eh->ether_shost);
+				return QCA_MULTI_LINK_PKT_DROP;
+			}
+
+			QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+				FL("shost %pM dhost %pM \n"), eh->ether_shost, eh->ether_dhost);
+
+			/*
+			 * For packets destined to sources on RootAP directly enq to STA vap.
+			 */
+			dev_hold(ap_dev);
+			ap_dev->netdev_ops->ndo_start_xmit(nbuf, ap_dev);
+			dev_put(ap_dev);
+			return QCA_MULTI_LINK_PKT_CONSUMED;
+		}
+	}
+	return QCA_MULTI_LINK_PKT_ALLOW;
+}
+
+/**
+ * qca_multi_link_primary_sta_rx() - Processing for frames recieved on primary station vap
+ * @net_dev: station net device
+ * @nbuf: frame
+ *
+ * Return: @qca_multi_link_status_t
+ *	   QCA_MULTI_LINK_PKT_ALLOW: frame should be processed further by caller
+ *	   QCA_MULTI_LINK_PKT_DROP: frame to be dropped
+ *	   QCA_MULTI_LINK_PKT_CONSUMED: frame is consumed.
+ */
+static qca_multi_link_status_t qca_multi_link_primary_sta_rx(struct net_device *net_dev, qdf_nbuf_t nbuf)
+{
+	uint8_t is_mcast;
+	qca_multi_link_tbl_entry_t qca_ml_entry;
+	struct wiphy *sta_wiphy = NULL;
+	struct net_device *sta_dev = net_dev;
+	QDF_STATUS qal_status = QDF_STATUS_E_FAILURE;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	sta_wiphy = sta_dev->ieee80211_ptr->wiphy;
+	is_mcast = IEEE80211_IS_MULTICAST(eh->ether_dhost);
+
+	/*
+	 * Unicast Packets are allowed without any processing on Primary Station Vap.
+	 */
+	if (!is_mcast) {
+		return QCA_MULTI_LINK_PKT_ALLOW;
+	}
+
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("Primary STA Rx: always_primary=%d, loop_detected=%d,\
+			drop_secondary_mcast=%d, shost %pM dhost %pM is_mcast=%d\n"),
+			qca_multi_link_cfg.always_primary, qca_multi_link_cfg.loop_detected,
+			qca_multi_link_cfg.drop_secondary_mcast, eh->ether_shost, eh->ether_dhost, is_mcast);
+
+	/*
+	 * Mcast packet handling on Primary Station Vap Interface.
+	 */
+	qca_ml_entry.qal_fdb_ieee80211_ptr = NULL;
+	qca_ml_entry.qal_fdb_dev = NULL;
+	qca_ml_entry.qal_fdb_is_local = 0;
+	qal_status = qca_multi_link_tbl_has_entry(sta_dev, eh->ether_shost, 0,
+				&qca_ml_entry);
+	if (qal_status != QDF_STATUS_SUCCESS) {
+		if (qca_multi_link_cfg.loop_detected) {
+		/*
+		 * If there is no fdb entry, the we allow the packet to go to
+		 * bridge as this might be the first packet from any device
+		 * on the RootAP side.
+		 */
+			return QCA_MULTI_LINK_PKT_ALLOW;
+		}
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	/*
+	 * Case 1:
+	 * ieee80211_ptr pointer being NULL indicates that the port
+	 * corresponding to the fdb entry is a non-wireless/ethernet
+	 * device behind the repeater and the packet is a mcast looped packet.
+	 * Case 2:
+	 * ieee80211_ptr pointer being non NULL indicates that the source
+	 * corresponding to the fdb entry is a wireless device
+	 * behind the repeater and the packet is a mcast looped packet.
+	 */
+
+	/*
+	 * Drop the loopback mcast packets from ethernet devices behind the repeater.
+	 */
+	if (!qca_ml_entry.qal_fdb_ieee80211_ptr) {
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	if (qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy != sta_wiphy) {
+		if (!qca_multi_link_cfg.loop_detected) {
+			if (qca_ml_entry.qal_fdb_is_local
+	&& (qca_ml_entry.qal_fdb_ieee80211_ptr->iftype == NL80211_IFTYPE_STATION)) {
+				qca_multi_link_cfg.loop_detected = true;
+				QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_INFO,
+						FL("\n****Wifi Rptr Loop Detected****\n"));
+			}
+		}
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	/*
+	 * If the mac-address is learnt on the Primary station in the bridge,
+	 * then the mcast packet is from a source on the RootAP side and we
+	 * should allow the packet.
+	 */
+	if (qca_ml_entry.qal_fdb_dev == sta_dev) {
+		return QCA_MULTI_LINK_PKT_ALLOW;
+	}
+
+	return QCA_MULTI_LINK_PKT_DROP;
+}
+
+/**
+ * qca_multi_link_sta_rx() - Processing for frames recieved on STA VAP
+ * @net_dev: station net device
+ * @nbuf: frame
+ *
+ * Return: false: frame not consumed and should be processed further by caller
+ *	   true: frame dropped/enqueued.
+ */
+bool qca_multi_link_sta_rx(struct net_device *net_dev, qdf_nbuf_t nbuf,
+			  qca_multi_link_needs_enq_t allow_vap_enq)
+{
+	uint8_t is_eapol;
+	bool is_primary = false;
+	struct wiphy *sta_wiphy = NULL;
+	struct net_device *sta_dev = net_dev;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+	bool drop_packet = false;
+	qca_multi_link_status_t status = QCA_MULTI_LINK_PKT_NONE;
+
+	if (!qca_multi_link_need_procesing()) {
+		goto end;
+	}
+
+	is_eapol = (eh->ether_type == htons(ETHERTYPE_PAE));
+	if (is_eapol) {
+		goto end;
+	}
+
+	sta_wiphy = sta_dev->ieee80211_ptr->wiphy;
+	is_primary = qca_multi_link_is_primary_radio(sta_wiphy);
+
+	if (qca_multi_link_drop_always_primary(is_primary, nbuf)) {
+		drop_packet = true;
+		goto end;
+	}
+
+	dev_hold(sta_dev);
+	if (is_primary) {
+		status = qca_multi_link_primary_sta_rx(sta_dev, nbuf);
+	} else {
+		status = qca_multi_link_secondary_sta_rx(sta_dev, nbuf, allow_vap_enq);
+	}
+	dev_put(sta_dev);
+
+	if (status == QCA_MULTI_LINK_PKT_ALLOW) {
+		goto end;
+	} else if (status == QCA_MULTI_LINK_PKT_CONSUMED) {
+		return true;
+	} else if (status == QCA_MULTI_LINK_PKT_DROP) {
+		drop_packet = true;
+	}
+
+end:
+	if (drop_packet) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("\n STA TX - Drop Packet for Mac=%pM\n"), eh->ether_shost);
+		qdf_nbuf_free(nbuf);
+		return true;
+	}
+	return false;
+}
+
+qdf_export_symbol(qca_multi_link_sta_rx);
+
+/**
+ * qca_multi_link_secondary_sta_tx() - Repeater TX processing for Secondary
+ * @net_device: station net device handle
+ * @nbuf: frame
+ *
+ * Return: @qca_multi_link_status_t
+ *	   QCA_MULTI_LINK_PKT_ALLOW: frame should be processed further by caller
+ *	   QCA_MULTI_LINK_PKT_DROP: frame to be dropped
+ *	   QCA_MULTI_LINK_PKT_CONSUMED: frame is consumed.
+ */
+static qca_multi_link_status_t qca_multi_link_secondary_sta_tx(struct net_device *net_dev, qdf_nbuf_t nbuf)
+{
+	uint8_t is_mcast;
+	struct wiphy *sta_wiphy = NULL;
+	struct net_device *sta_dev = net_dev;
+	qca_multi_link_tbl_entry_t qca_ml_entry;
+	QDF_STATUS qal_status = QDF_STATUS_E_FAILURE;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	if (qca_multi_link_drop_always_primary(false, nbuf)) {
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	sta_wiphy = sta_dev->ieee80211_ptr->wiphy;
+
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("STA Secondary Tx: always_primary=%d, loop_detected=%d,\
+		drop_secondary_mcast=%d, shost %pM dhost %pM \n"),
+		qca_multi_link_cfg.always_primary, qca_multi_link_cfg.loop_detected, qca_multi_link_cfg.drop_secondary_mcast,
+		eh->ether_shost, eh->ether_dhost);
+
+	/*
+	 * For a Secondary station, only Packets from clients on the same band are allowed for transmit.
+	 */
+	is_mcast = IEEE80211_IS_MULTICAST(eh->ether_dhost);
+	qca_ml_entry.qal_fdb_ieee80211_ptr = NULL;
+	qca_ml_entry.qal_fdb_dev = NULL;
+	qca_ml_entry.qal_fdb_is_local = 0;
+	qal_status = qca_multi_link_tbl_has_entry(sta_dev, eh->ether_shost, 0,
+				       &qca_ml_entry);
+	if (qal_status != QDF_STATUS_SUCCESS) {
+		if (!is_mcast) {
+		return QCA_MULTI_LINK_PKT_ALLOW;
+		}
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	if (!qca_ml_entry.qal_fdb_ieee80211_ptr) {
+		/*
+		 * ieee80211_ptr pointer will be NULL for ethernet devices.
+		 * Packets from ethernet devices or bridge are allowed only on	Primary radio.
+		 */
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	/*
+	 * Do the DBDC Fast Lane processing at the beginning and then fall back to normal DBDC STA TX
+	 * if either fast-lane is disabled or the TX is for non-fast lane radio.
+	 */
+	if (is_fast_lane_radio(sta_wiphy) && is_fast_lane_radio(qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy)) {
+		return QCA_MULTI_LINK_PKT_ALLOW;
+	}
+
+	if (qca_multi_link_drop_secondary_mcast(nbuf)) {
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	/*
+	 * Compare the physical device and check if the source is a client
+	 * on the same radio.
+	 */
+	if (qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy != sta_dev->ieee80211_ptr->wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("STA TX: Diff Band Primary drop\
+			shost %pM dhost %pM \n"), eh->ether_shost, eh->ether_dhost);
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	return QCA_MULTI_LINK_PKT_ALLOW;
+}
+
+/**
+ * qca_multi_link_primary_sta_tx() - Repeater TX processing for Primary
+ * @net_device: station net device handle
+ * @nbuf: frame
+ *
+ * Return: @qca_multi_link_status_t
+ *	   QCA_MULTI_LINK_PKT_ALLOW: frame should be processed further by caller
+ *	   QCA_MULTI_LINK_PKT_DROP: frame to be dropped
+ *	   QCA_MULTI_LINK_PKT_CONSUMED: frame is consumed.
+ */
+static qca_multi_link_status_t qca_multi_link_primary_sta_tx(struct net_device *net_dev, qdf_nbuf_t nbuf)
+{
+	struct wiphy *sta_wiphy = NULL;
+	struct net_device *sta_dev = net_dev;
+	qca_multi_link_tbl_entry_t qca_ml_entry;
+	QDF_STATUS qal_status = QDF_STATUS_E_FAILURE;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+
+	sta_wiphy = sta_dev->ieee80211_ptr->wiphy;
+
+	QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("Primary STA Tx: always_primary=%d, loop_detected=%d,\
+				drop_secondary_mcast=%d, shost %pM dhost %pM \n"),
+				qca_multi_link_cfg.always_primary, qca_multi_link_cfg.loop_detected,
+				qca_multi_link_cfg.drop_secondary_mcast, eh->ether_shost, eh->ether_dhost);
+
+	/*
+	 * For Primary station, packets allowed for transmission are:
+	 * 1) Packets from ethernet devices.
+	 * 2) Packets from radios which are not participating in backhaul.
+	 * 3) Packets from clients on the same band.
+	 */
+	qca_ml_entry.qal_fdb_ieee80211_ptr = NULL;
+	qca_ml_entry.qal_fdb_dev = NULL;
+	qca_ml_entry.qal_fdb_is_local = 0;
+	qal_status = qca_multi_link_tbl_has_entry(sta_dev, eh->ether_shost, 0,
+						&qca_ml_entry);
+	/*
+	 * All packets coming to Primary station has to have a bridge fdb entry
+	 * as they will be received from bridge only.
+	 */
+	if (qal_status != QDF_STATUS_SUCCESS) {
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	if (!qca_ml_entry.qal_fdb_ieee80211_ptr) {
+		/*
+		 * ieee80211_ptr pointer will be NULL for ethernet devices.
+		 * Packets from ethernet devices or bridge are allowed only on	Primary radio.
+		 */
+		return QCA_MULTI_LINK_PKT_ALLOW;
+	}
+
+	/*
+	 * Do the DBDC Fast Lane processing at the beginning and then fall back to normal DBDC STA TX
+	 * if either fast-lane is disabled or the TX is for non-fast lane radio.
+	 */
+	if (is_fast_lane_radio(sta_wiphy)
+		&& is_fast_lane_radio(qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy)) {
+		return QCA_MULTI_LINK_PKT_ALLOW;
+	}
+
+	/*
+	 * This flag will be set for radios which does not particpate in backhaul.
+	 */
+	if (is_no_backhaul_radio(qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy)) {
+		return QCA_MULTI_LINK_PKT_ALLOW;
+	}
+
+	/*
+	 * Compare the physical device and check if the source is a client
+	 * on the same radio.
+	 */
+	if (qca_ml_entry.qal_fdb_ieee80211_ptr->wiphy != sta_dev->ieee80211_ptr->wiphy) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG, FL("STA TX: Diff Band Primary drop\
+			shost %pM dhost %pM \n"), eh->ether_shost, eh->ether_dhost);
+		return QCA_MULTI_LINK_PKT_DROP;
+	}
+
+	return QCA_MULTI_LINK_PKT_ALLOW;
+}
+
+/**
+ * qca_multi_link_sta_tx() - Repeater TX processing
+ * @net_device: station net device handle
+ * @nbuf: frame
+ *
+ * Return: false: frame not consumed and should be processed further by caller
+ *	   true: frame consumed
+ */
+bool qca_multi_link_sta_tx(struct net_device *net_dev, qdf_nbuf_t nbuf)
+{
+	bool drop_packet = false;
+	bool is_primary = false;
+	struct wiphy *sta_wiphy = NULL;
+	struct net_device *sta_dev = net_dev;
+	qdf_ether_header_t *eh = (qdf_ether_header_t *) qdf_nbuf_data(nbuf);
+	qca_multi_link_status_t status = QCA_MULTI_LINK_PKT_NONE;
+
+	if (!qca_multi_link_need_procesing()) {
+		goto end;
+	}
+
+	if (!qca_multi_link_cfg.loop_detected) {
+		goto end;
+	}
+
+	if (qca_multi_link_pktfrom_ownsrc(sta_dev, nbuf)) {
+		goto end;
+	}
+
+	sta_wiphy = sta_dev->ieee80211_ptr->wiphy;
+	is_primary = qca_multi_link_is_primary_radio(sta_wiphy);
+
+	dev_hold(net_dev);
+	if (is_primary) {
+		status = qca_multi_link_primary_sta_tx(sta_dev, nbuf);
+	} else {
+		status = qca_multi_link_secondary_sta_tx(sta_dev, nbuf);
+	}
+	dev_put(net_dev);
+
+	if (status == QCA_MULTI_LINK_PKT_ALLOW) {
+		goto end;
+	} else if (status == QCA_MULTI_LINK_PKT_CONSUMED) {
+		return true;
+	} else if (status == QCA_MULTI_LINK_PKT_DROP) {
+		drop_packet = true;
+	}
+
+end:
+	if (drop_packet) {
+		QDF_TRACE(QDF_MODULE_ID_RPTR, QDF_TRACE_LEVEL_DEBUG,
+			FL("\n STA TX - Drop Packet for Mac=%pM\n"), eh->ether_shost);
+		qdf_nbuf_free(nbuf);
+		return true;
+	}
+
+	return false;
+}
+
+qdf_export_symbol(qca_multi_link_sta_tx);
diff --git a/qca_multi_link/src/qca_multi_link_tbl.c b/qca_multi_link/src/qca_multi_link_tbl.c
new file mode 100644
index 0000000..7d7d98e
--- /dev/null
+++ b/qca_multi_link/src/qca_multi_link_tbl.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. 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 above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "qca_multi_link_tbl.h"
+#include "qdf_module.h"
+#include "qdf_trace.h"
+
+int qca_multi_link_tbl_get_eth_entries(struct net_device *net_dev,
+					void *fill_buff, int buff_size)
+{
+	struct net_bridge_fdb_entry *search_fdb = NULL;
+	int i;
+	struct net_bridge_port *p = br_port_get_rcu(net_dev);
+	struct net_device *ndev = NULL;
+	struct wireless_dev *wdev = NULL;
+	int num_of_entries = 0;
+	qca_multi_link_tbl_entry_t *qfdb = (qca_multi_link_tbl_entry_t *)fill_buff;
+	int fdb_entry_size = sizeof(qca_multi_link_tbl_entry_t);
+
+	if (!p) {
+		qdf_err("bridge port is NULL or disabled for dev %p \n", net_dev);
+		return 0;
+	}
+
+	if (buff_size < fdb_entry_size) {
+		qdf_err("Buffer size cannot be less than size of entry:%d dev:%p\n", fdb_entry_size, net_dev);
+		return 0;
+	}
+
+	/*
+	 * Traverse the bridge hah to get all ethernet interface entries.
+	 */
+	rcu_read_lock();
+	for (i = 0; i < BR_HASH_SIZE ; i++) {
+		hlist_for_each_entry_rcu(search_fdb, &p->br->hash[i], hlist) {
+			ndev = search_fdb->dst ? search_fdb->dst->dev : NULL;
+			wdev = ndev ? ndev->ieee80211_ptr : NULL;
+			if (!wdev && ndev) {
+				memcpy(qfdb->qal_mac_addr, search_fdb->addr.addr, 6);
+				qfdb->qal_fdb_dev = ndev;
+				qfdb->qal_fdb_is_local =  search_fdb->is_local;
+				num_of_entries++;
+				qfdb += 1;
+				buff_size -= fdb_entry_size;
+				if (buff_size < fdb_entry_size) {
+					rcu_read_unlock();
+					return num_of_entries;
+				}
+			}
+		}
+	}
+	rcu_read_unlock();
+
+	return num_of_entries;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_get_eth_entries);
+
+struct net_device *qca_multi_link_tbl_find_sta_or_ap(struct net_device *net_dev,
+					uint8_t dev_type)
+{
+	struct net_bridge_fdb_entry *search_fdb = NULL;
+	struct net_device *search_dev = NULL;
+	struct wireless_dev	*ieee80211_ptr = NULL;
+	int i;
+	enum nl80211_iftype search_if_type;
+	struct net_bridge_port *p = br_port_get_rcu(net_dev);
+
+	if (!p || (p && p->state == BR_STATE_DISABLED))
+		return NULL;
+
+	if (!dev_type)
+		search_if_type = NL80211_IFTYPE_AP;
+	else
+		search_if_type = NL80211_IFTYPE_STATION;
+
+	rcu_read_lock();
+	for (i = 0; i < BR_HASH_SIZE; i++) {
+		hlist_for_each_entry_rcu(search_fdb, &p->br->hash[i], hlist) {
+			if (!search_fdb->is_local)
+				continue;
+
+			search_dev = search_fdb->dst->dev;
+			ieee80211_ptr = search_dev->ieee80211_ptr;
+			if (ieee80211_ptr
+		&& (ieee80211_ptr->iftype == search_if_type)
+		&& (ieee80211_ptr->wiphy == net_dev->ieee80211_ptr->wiphy)) {
+				rcu_read_unlock();
+				return search_dev;
+			}
+		}
+	}
+
+	rcu_read_unlock();
+	return NULL;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_find_sta_or_ap);
+
+QDF_STATUS qca_multi_link_tbl_delete_entry(struct net_device *net_dev, uint8_t *addr)
+{
+	int status;
+	struct net_bridge_fdb_entry *fdb_entry = NULL;
+
+	fdb_entry = br_fdb_has_entry(net_dev, addr, 0);
+	if (!fdb_entry) {
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (fdb_entry->is_local) {
+		return QDF_STATUS_SUCCESS;
+	}
+
+	status = br_fdb_delete_by_netdev(net_dev, addr, 0);
+	if (status < 0) {
+		return QDF_STATUS_E_FAILURE;
+	}
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_delete_entry);
+
+QDF_STATUS qca_multi_link_tbl_has_entry(struct net_device *net_dev,
+				const char *addr, uint16_t vlan_id,
+				qca_multi_link_tbl_entry_t *qca_ml_entry)
+{
+	struct net_bridge_fdb_entry *fdb_entry = NULL;
+	struct net_bridge_port *fdb_port = NULL;
+	struct net_device *fdb_dev = NULL;
+
+	if (!qca_ml_entry)
+		return QDF_STATUS_E_FAILURE;
+
+	fdb_entry = br_fdb_has_entry(net_dev, addr, vlan_id);
+	if (!fdb_entry)
+		return QDF_STATUS_E_FAILURE;
+
+	fdb_port = fdb_entry->dst;
+	if (!fdb_port) {
+		qdf_err("bridge port is NULL for mac-addr %pM\n", addr);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	fdb_dev = fdb_port->dev;
+	if (!fdb_dev) {
+		qdf_err("bridge netdev is NULL for mac-addr %pM\n", addr);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	qca_ml_entry->qal_fdb_ieee80211_ptr = fdb_dev->ieee80211_ptr;
+	qca_ml_entry->qal_fdb_dev = fdb_dev;
+	qca_ml_entry->qal_fdb_is_local = fdb_entry->is_local;
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_has_entry);
+
+QDF_STATUS qca_multi_link_tbl_register_update_notifier(void *nb)
+{
+	struct notifier_block *notifier = (struct notifier_block *)nb;
+
+	if (!notifier) {
+		qdf_err("Bridge Notifier is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	br_fdb_update_register_notify(notifier);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_register_update_notifier);
+
+QDF_STATUS qca_multi_link_tbl_unregister_update_notifier(void *nb)
+{
+	struct notifier_block *notifier = (struct notifier_block *)nb;
+
+	if (!notifier) {
+		qdf_err("Bridge Notifier is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	br_fdb_update_unregister_notify(notifier);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_unregister_update_notifier);
+
+QDF_STATUS qca_multi_link_tbl_register_notifier(void *nb)
+{
+	struct notifier_block *notifier = (struct notifier_block *)nb;
+
+	if (!notifier) {
+		qdf_err("Bridge Notifier is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	br_fdb_register_notify(notifier);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_register_notifier);
+
+QDF_STATUS qca_multi_link_tbl_unregister_notifier(void *nb)
+{
+	struct notifier_block *notifier = (struct notifier_block *)nb;
+
+	if (!notifier) {
+		qdf_err("Bridge Notifier is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	br_fdb_unregister_notify(notifier);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_unregister_notifier);
+
+struct net_device *qca_multi_link_tbl_get_bridge_dev(struct net_device *port_dev)
+{
+	struct net_bridge_port *fdb_port = NULL;
+	struct net_bridge *br = NULL;
+
+	if (!port_dev) {
+		qdf_err("Port dev is NULL");
+		return NULL;
+	}
+
+	fdb_port = br_port_get_rcu(port_dev);
+	if (!fdb_port) {
+		qdf_err("fdb port is NULL");
+		return NULL;
+	}
+
+	br = fdb_port->br;
+	if (!br) {
+		qdf_err("bridge dev is NULL");
+		return NULL;
+	}
+
+	return br->dev;
+}
+
+qdf_export_symbol(qca_multi_link_tbl_get_bridge_dev);
diff --git a/tools/linux/cfg80211_ven_cmd.h b/tools/linux/cfg80211_ven_cmd.h
index 3857182..c02ef39 100644
--- a/tools/linux/cfg80211_ven_cmd.h
+++ b/tools/linux/cfg80211_ven_cmd.h
@@ -1188,6 +1188,9 @@
 	OL_ATH_PARAM_NON_INHERIT_ENABLE = 443,
 	/* Set/Get next frequency for radar */
 	OL_ATH_PARAM_NXT_RDR_FREQ = 444,
+	/* set the flag for a radio with no backhaul */
+	OL_ATH_PARAM_NO_BACKHAUL_RADIO = 445,
+
 };
 
 #ifdef CONFIG_SUPPORT_LIBROXML
@@ -3071,6 +3074,10 @@
 		OL_ATH_PARAM_SHIFT | OL_ATH_PARAM_NON_INHERIT_ENABLE, SET_PARAM, 1},
 	{"g_non_inherit_enable",
 		OL_ATH_PARAM_SHIFT | OL_ATH_PARAM_NON_INHERIT_ENABLE, GET_PARAM, 0},
+	{"nobckhlradio",
+		OL_ATH_PARAM_SHIFT | OL_ATH_PARAM_NO_BACKHAUL_RADIO, SET_PARAM, 1},
+	{"g_nobckhlradio",
+		OL_ATH_PARAM_SHIFT | OL_ATH_PARAM_NO_BACKHAUL_RADIO, GET_PARAM, 0},
 };
 #endif
 #endif