qca-wifi: Multi-Link Repeater Functionality

This change implements the multi-link WiFi repeater functionality
using the Linux bridge Layer 2 forwarding table.

Change-Id: Ifa862a139f618f6bffde5613fd8d8d2e08d4f106
This commit is contained in:
Subhranil Choudhury
2020-05-04 16:58:44 +05:30
والد 5fbb744f99
کامیت f34bab46e4
5فایلهای تغییر یافته به همراه1718 افزوده شده و 0 حذف شده

مشاهده پرونده

@@ -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

مشاهده پرونده

@@ -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 را بارگزاری کن

مشاهده پرونده

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