
when secondary unicast from station takes rx_to_tx path and reaches host, enqueue directly to nss ap node. Change-Id: I03b3e9488683376fa9a12c574ba230ebaa55c98d
1214 рядки
35 KiB
C
1214 рядки
35 KiB
C
/*
|
|
* 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)
|
|
{
|
|
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) {
|
|
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
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);
|