Файли
android_kernel_samsung_sm86…/qca_multi_link/src/qca_multi_link.c
syed touqeer pasha 0107d0ca4a qca-wifi: Extap mode secondary radio unicast packet handling
when secondary unicast from station takes rx_to_tx path
and reaches host, enqueue directly to nss ap node.

Change-Id: I03b3e9488683376fa9a12c574ba230ebaa55c98d
2020-07-22 00:20:46 +05:30

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);