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:
103
qca_multi_link/inc/qca_multi_link.h
Normal file
103
qca_multi_link/inc/qca_multi_link.h
Normal file
@@ -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
|
129
qca_multi_link/inc/qca_multi_link_tbl.h
Normal file
129
qca_multi_link/inc/qca_multi_link_tbl.h
Normal file
@@ -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 */
|
1219
qca_multi_link/src/qca_multi_link.c
Normal file
1219
qca_multi_link/src/qca_multi_link.c
Normal file
تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
Diff را بارگزاری کن
260
qca_multi_link/src/qca_multi_link_tbl.c
Normal file
260
qca_multi_link/src/qca_multi_link_tbl.c
Normal file
@@ -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);
|
مرجع در شماره جدید
Block a user