mirror of
https://github.com/RinCat/RTL88x2BU-Linux-Driver.git
synced 2026-01-29 07:16:35 +00:00
Update to 5.13.1 from upstream
This commit is contained in:
786
core/wds/rtw_wds.c
Normal file
786
core/wds/rtw_wds.c
Normal file
@@ -0,0 +1,786 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright(c) 2007 - 2017 Realtek Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*****************************************************************************/
|
||||
#define _RTW_WDS_C_
|
||||
|
||||
#include <drv_types.h>
|
||||
|
||||
#if defined(CONFIG_RTW_WDS)
|
||||
#include <linux/jhash.h>
|
||||
|
||||
#if defined(CONFIG_AP_MODE)
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
|
||||
static void rtw_wpath_free_rcu(struct rtw_wds_path *wpath)
|
||||
{
|
||||
kfree_rcu(wpath, rcu);
|
||||
rtw_mstat_update(MSTAT_TYPE_PHY, MSTAT_FREE, sizeof(struct rtw_wds_path));
|
||||
}
|
||||
#else
|
||||
static void rtw_wpath_free_rcu_callback(rtw_rcu_head *head)
|
||||
{
|
||||
struct rtw_wds_path *wpath;
|
||||
|
||||
wpath = container_of(head, struct rtw_wds_path, rcu);
|
||||
rtw_mfree(wpath, sizeof(struct rtw_wds_path));
|
||||
}
|
||||
|
||||
static void rtw_wpath_free_rcu(struct rtw_wds_path *wpath)
|
||||
{
|
||||
call_rcu(&wpath->rcu, rtw_wpath_free_rcu_callback);
|
||||
}
|
||||
#endif
|
||||
#endif /* PLATFORM_LINUX */
|
||||
|
||||
static void rtw_wds_path_free_rcu(struct rtw_wds_table *tbl, struct rtw_wds_path *wpath);
|
||||
|
||||
static u32 rtw_wds_table_hash(const void *addr, u32 len, u32 seed)
|
||||
{
|
||||
/* Use last four bytes of hw addr as hash index */
|
||||
return jhash_1word(*(u32 *)(addr+2), seed);
|
||||
}
|
||||
|
||||
static const rtw_rhashtable_params rtw_wds_rht_params = {
|
||||
.nelem_hint = 2,
|
||||
.automatic_shrinking = true,
|
||||
.key_len = ETH_ALEN,
|
||||
.key_offset = offsetof(struct rtw_wds_path, dst),
|
||||
.head_offset = offsetof(struct rtw_wds_path, rhash),
|
||||
.hashfn = rtw_wds_table_hash,
|
||||
};
|
||||
|
||||
static void rtw_wds_path_rht_free(void *ptr, void *tblptr)
|
||||
{
|
||||
struct rtw_wds_path *wpath = ptr;
|
||||
struct rtw_wds_table *tbl = tblptr;
|
||||
|
||||
rtw_wds_path_free_rcu(tbl, wpath);
|
||||
}
|
||||
|
||||
static struct rtw_wds_table *rtw_wds_table_alloc(void)
|
||||
{
|
||||
struct rtw_wds_table *newtbl;
|
||||
|
||||
newtbl = rtw_malloc(sizeof(struct rtw_wds_table));
|
||||
if (!newtbl)
|
||||
return NULL;
|
||||
|
||||
return newtbl;
|
||||
}
|
||||
|
||||
static void rtw_wds_table_free(struct rtw_wds_table *tbl)
|
||||
{
|
||||
rtw_rhashtable_free_and_destroy(&tbl->rhead,
|
||||
rtw_wds_path_rht_free, tbl);
|
||||
rtw_mfree(tbl, sizeof(struct rtw_wds_table));
|
||||
}
|
||||
|
||||
void rtw_wds_path_assign_nexthop(struct rtw_wds_path *wpath, struct sta_info *sta)
|
||||
{
|
||||
rtw_rcu_assign_pointer(wpath->next_hop, sta);
|
||||
}
|
||||
|
||||
static struct rtw_wds_path *rtw_wpath_lookup(struct rtw_wds_table *tbl, const u8 *dst)
|
||||
{
|
||||
struct rtw_wds_path *wpath;
|
||||
|
||||
if (!tbl)
|
||||
return NULL;
|
||||
|
||||
wpath = rtw_rhashtable_lookup_fast(&tbl->rhead, dst, rtw_wds_rht_params);
|
||||
|
||||
return wpath;
|
||||
}
|
||||
|
||||
struct rtw_wds_path *rtw_wds_path_lookup(_adapter *adapter, const u8 *dst)
|
||||
{
|
||||
return rtw_wpath_lookup(adapter->wds_paths, dst);
|
||||
}
|
||||
|
||||
static struct rtw_wds_path *
|
||||
__rtw_wds_path_lookup_by_idx(struct rtw_wds_table *tbl, int idx)
|
||||
{
|
||||
int i = 0, ret;
|
||||
struct rtw_wds_path *wpath = NULL;
|
||||
rtw_rhashtable_iter iter;
|
||||
|
||||
if (!tbl)
|
||||
return NULL;
|
||||
|
||||
ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
ret = rtw_rhashtable_walk_start(&iter);
|
||||
if (ret && ret != -EAGAIN)
|
||||
goto err;
|
||||
|
||||
while ((wpath = rtw_rhashtable_walk_next(&iter))) {
|
||||
if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
|
||||
continue;
|
||||
if (IS_ERR(wpath))
|
||||
break;
|
||||
if (i++ == idx)
|
||||
break;
|
||||
}
|
||||
err:
|
||||
rtw_rhashtable_walk_stop(&iter);
|
||||
rtw_rhashtable_walk_exit(&iter);
|
||||
|
||||
if (IS_ERR(wpath) || !wpath)
|
||||
return NULL;
|
||||
|
||||
return wpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locking: must be called within a read rcu section.
|
||||
*/
|
||||
struct rtw_wds_path *
|
||||
rtw_wds_path_lookup_by_idx(_adapter *adapter, int idx)
|
||||
{
|
||||
return __rtw_wds_path_lookup_by_idx(adapter->wds_paths, idx);
|
||||
}
|
||||
|
||||
void dump_wpath(void *sel, _adapter *adapter)
|
||||
{
|
||||
struct rtw_wds_path *wpath;
|
||||
int idx = 0;
|
||||
char dst[ETH_ALEN];
|
||||
char next_hop[ETH_ALEN];
|
||||
u32 age_ms;
|
||||
|
||||
RTW_PRINT_SEL(sel, "num:%d\n", ATOMIC_READ(&adapter->wds_path_num));
|
||||
RTW_PRINT_SEL(sel, "%-17s %-17s %-6s\n"
|
||||
, "dst", "next_hop", "age"
|
||||
);
|
||||
|
||||
do {
|
||||
rtw_rcu_read_lock();
|
||||
|
||||
wpath = rtw_wds_path_lookup_by_idx(adapter, idx);
|
||||
if (wpath) {
|
||||
_rtw_memcpy(dst, wpath->dst, ETH_ALEN);
|
||||
_rtw_memcpy(next_hop, wpath->next_hop->cmn.mac_addr, ETH_ALEN);
|
||||
age_ms = rtw_get_passing_time_ms(wpath->last_update);
|
||||
}
|
||||
|
||||
rtw_rcu_read_unlock();
|
||||
|
||||
if (wpath) {
|
||||
RTW_PRINT_SEL(sel, MAC_FMT" "MAC_FMT" %6u\n"
|
||||
, MAC_ARG(dst), MAC_ARG(next_hop)
|
||||
, age_ms < 999999 ? age_ms : 999999
|
||||
);
|
||||
}
|
||||
|
||||
idx++;
|
||||
} while (wpath);
|
||||
}
|
||||
|
||||
static
|
||||
struct rtw_wds_path *rtw_wds_path_new(_adapter *adapter,
|
||||
const u8 *dst)
|
||||
{
|
||||
struct rtw_wds_path *new_wpath;
|
||||
|
||||
new_wpath = rtw_zmalloc(sizeof(struct rtw_wds_path));
|
||||
if (!new_wpath)
|
||||
return NULL;
|
||||
|
||||
new_wpath->adapter = adapter;
|
||||
_rtw_memcpy(new_wpath->dst, dst, ETH_ALEN);
|
||||
new_wpath->last_update = rtw_get_current_time();
|
||||
|
||||
return new_wpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: 0 on success
|
||||
*
|
||||
* State: the initial state of the new path is set to 0
|
||||
*/
|
||||
struct rtw_wds_path *rtw_wds_path_add(_adapter *adapter,
|
||||
const u8 *dst, struct sta_info *next_hop)
|
||||
{
|
||||
struct rtw_wds_table *tbl = adapter->wds_paths;
|
||||
struct rtw_wds_path *wpath, *new_wpath;
|
||||
int ret;
|
||||
|
||||
if (!tbl)
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
|
||||
if (_rtw_memcmp(dst, adapter_mac_addr(adapter), ETH_ALEN) == _TRUE)
|
||||
/* never add ourselves as neighbours */
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
|
||||
if (IS_MCAST(dst))
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
|
||||
if (ATOMIC_INC_UNLESS(&adapter->wds_path_num, RTW_WDS_MAX_PATHS) == 0)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
|
||||
new_wpath = rtw_wds_path_new(adapter, dst);
|
||||
if (!new_wpath)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
do {
|
||||
ret = rtw_rhashtable_lookup_insert_fast(&tbl->rhead,
|
||||
&new_wpath->rhash,
|
||||
rtw_wds_rht_params);
|
||||
|
||||
if (ret == -EEXIST)
|
||||
wpath = rtw_rhashtable_lookup_fast(&tbl->rhead,
|
||||
dst,
|
||||
rtw_wds_rht_params);
|
||||
|
||||
} while (unlikely(ret == -EEXIST && !wpath));
|
||||
|
||||
if (ret && ret != -EEXIST)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/* At this point either new_wpath was added, or we found a
|
||||
* matching entry already in the table; in the latter case
|
||||
* free the unnecessary new entry.
|
||||
*/
|
||||
if (ret == -EEXIST) {
|
||||
rtw_mfree(new_wpath, sizeof(struct rtw_wds_path));
|
||||
new_wpath = wpath;
|
||||
}
|
||||
rtw_wds_path_assign_nexthop(new_wpath, next_hop);
|
||||
|
||||
return new_wpath;
|
||||
}
|
||||
|
||||
static void rtw_wds_path_free_rcu(struct rtw_wds_table *tbl,
|
||||
struct rtw_wds_path *wpath)
|
||||
{
|
||||
_adapter *adapter = wpath->adapter;
|
||||
|
||||
ATOMIC_DEC(&adapter->wds_path_num);
|
||||
|
||||
rtw_wpath_free_rcu(wpath);
|
||||
}
|
||||
|
||||
static void __rtw_wds_path_del(struct rtw_wds_table *tbl, struct rtw_wds_path *wpath)
|
||||
{
|
||||
rtw_rhashtable_remove_fast(&tbl->rhead, &wpath->rhash, rtw_wds_rht_params);
|
||||
rtw_wds_path_free_rcu(tbl, wpath);
|
||||
}
|
||||
|
||||
void rtw_wds_path_flush_by_nexthop(struct sta_info *sta)
|
||||
{
|
||||
_adapter *adapter = sta->padapter;
|
||||
struct rtw_wds_table *tbl = adapter->wds_paths;
|
||||
struct rtw_wds_path *wpath;
|
||||
rtw_rhashtable_iter iter;
|
||||
int ret;
|
||||
|
||||
if (!tbl)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_start(&iter);
|
||||
if (ret && ret != -EAGAIN)
|
||||
goto out;
|
||||
|
||||
while ((wpath = rtw_rhashtable_walk_next(&iter))) {
|
||||
if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
|
||||
continue;
|
||||
if (IS_ERR(wpath))
|
||||
break;
|
||||
|
||||
if (rtw_rcu_access_pointer(wpath->next_hop) == sta)
|
||||
__rtw_wds_path_del(tbl, wpath);
|
||||
}
|
||||
out:
|
||||
rtw_rhashtable_walk_stop(&iter);
|
||||
rtw_rhashtable_walk_exit(&iter);
|
||||
}
|
||||
|
||||
static void rtw_wds_table_flush_by_iface(struct rtw_wds_table *tbl)
|
||||
{
|
||||
struct rtw_wds_path *wpath;
|
||||
rtw_rhashtable_iter iter;
|
||||
int ret;
|
||||
|
||||
if (!tbl)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_start(&iter);
|
||||
if (ret && ret != -EAGAIN)
|
||||
goto out;
|
||||
|
||||
while ((wpath = rtw_rhashtable_walk_next(&iter))) {
|
||||
if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
|
||||
continue;
|
||||
if (IS_ERR(wpath))
|
||||
break;
|
||||
__rtw_wds_path_del(tbl, wpath);
|
||||
}
|
||||
out:
|
||||
rtw_rhashtable_walk_stop(&iter);
|
||||
rtw_rhashtable_walk_exit(&iter);
|
||||
}
|
||||
|
||||
void rtw_wds_path_flush_by_iface(_adapter *adapter)
|
||||
{
|
||||
rtw_wds_table_flush_by_iface(adapter->wds_paths);
|
||||
}
|
||||
|
||||
static int rtw_wds_table_path_del(struct rtw_wds_table *tbl,
|
||||
const u8 *addr)
|
||||
{
|
||||
struct rtw_wds_path *wpath;
|
||||
|
||||
if (!tbl)
|
||||
return -ENXIO;
|
||||
|
||||
rtw_rcu_read_lock();
|
||||
wpath = rtw_rhashtable_lookup_fast(&tbl->rhead, addr, rtw_wds_rht_params);
|
||||
if (!wpath) {
|
||||
rtw_rcu_read_unlock();
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
__rtw_wds_path_del(tbl, wpath);
|
||||
rtw_rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtw_wds_path_del(_adapter *adapter, const u8 *addr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rtw_wds_table_path_del(adapter->wds_paths, addr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int rtw_wds_pathtbl_init(_adapter *adapter)
|
||||
{
|
||||
struct rtw_wds_table *tbl_path;
|
||||
int ret;
|
||||
|
||||
tbl_path = rtw_wds_table_alloc();
|
||||
if (!tbl_path)
|
||||
return -ENOMEM;
|
||||
|
||||
rtw_rhashtable_init(&tbl_path->rhead, &rtw_wds_rht_params);
|
||||
|
||||
ATOMIC_SET(&adapter->wds_path_num, 0);
|
||||
adapter->wds_paths = tbl_path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void rtw_wds_path_tbl_expire(_adapter *adapter,
|
||||
struct rtw_wds_table *tbl)
|
||||
{
|
||||
struct rtw_wds_path *wpath;
|
||||
rtw_rhashtable_iter iter;
|
||||
int ret;
|
||||
|
||||
if (!tbl)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_start(&iter);
|
||||
if (ret && ret != -EAGAIN)
|
||||
goto out;
|
||||
|
||||
while ((wpath = rtw_rhashtable_walk_next(&iter))) {
|
||||
if (IS_ERR(wpath) && PTR_ERR(wpath) == -EAGAIN)
|
||||
continue;
|
||||
if (IS_ERR(wpath))
|
||||
break;
|
||||
if (rtw_time_after(rtw_get_current_time(), wpath->last_update + RTW_WDS_PATH_EXPIRE))
|
||||
__rtw_wds_path_del(tbl, wpath);
|
||||
}
|
||||
|
||||
out:
|
||||
rtw_rhashtable_walk_stop(&iter);
|
||||
rtw_rhashtable_walk_exit(&iter);
|
||||
}
|
||||
|
||||
void rtw_wds_path_expire(_adapter *adapter)
|
||||
{
|
||||
rtw_wds_path_tbl_expire(adapter, adapter->wds_paths);
|
||||
}
|
||||
|
||||
void rtw_wds_pathtbl_unregister(_adapter *adapter)
|
||||
{
|
||||
if (adapter->wds_paths) {
|
||||
rtw_wds_table_free(adapter->wds_paths);
|
||||
adapter->wds_paths = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int rtw_wds_nexthop_lookup(_adapter *adapter, const u8 *da, u8 *ra)
|
||||
{
|
||||
struct rtw_wds_path *wpath;
|
||||
struct sta_info *next_hop;
|
||||
int err = -ENOENT;
|
||||
|
||||
rtw_rcu_read_lock();
|
||||
wpath = rtw_wds_path_lookup(adapter, da);
|
||||
|
||||
if (!wpath)
|
||||
goto endlookup;
|
||||
|
||||
next_hop = rtw_rcu_dereference(wpath->next_hop);
|
||||
if (next_hop) {
|
||||
_rtw_memcpy(ra, next_hop->cmn.mac_addr, ETH_ALEN);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
endlookup:
|
||||
rtw_rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_AP_MODE) */
|
||||
|
||||
/* WDS group adddressed proxy TX record */
|
||||
struct rtw_wds_gptr {
|
||||
u8 src[ETH_ALEN];
|
||||
systime last_update;
|
||||
rtw_rhash_head rhash;
|
||||
_adapter *adapter;
|
||||
rtw_rcu_head rcu;
|
||||
};
|
||||
|
||||
#define RTW_WDS_GPTR_EXPIRE (2 * HZ)
|
||||
|
||||
/* Maximum number of gptrs per interface */
|
||||
#define RTW_WDS_MAX_GPTRS 1024
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
|
||||
static void rtw_wgptr_free_rcu(struct rtw_wds_gptr *wgptr)
|
||||
{
|
||||
kfree_rcu(wgptr, rcu);
|
||||
rtw_mstat_update(MSTAT_TYPE_PHY, MSTAT_FREE, sizeof(struct rtw_wds_gptr));
|
||||
}
|
||||
#else
|
||||
static void rtw_wgptr_free_rcu_callback(rtw_rcu_head *head)
|
||||
{
|
||||
struct rtw_wds_gptr *wgptr;
|
||||
|
||||
wgptr = container_of(head, struct rtw_wds_gptr, rcu);
|
||||
rtw_mfree(wgptr, sizeof(struct rtw_wds_gptr));
|
||||
}
|
||||
|
||||
static void rtw_wgptr_free_rcu(struct rtw_wds_gptr *wgptr)
|
||||
{
|
||||
call_rcu(&wgptr->rcu, rtw_wgptr_free_rcu_callback);
|
||||
}
|
||||
#endif
|
||||
#endif /* PLATFORM_LINUX */
|
||||
|
||||
static void rtw_wds_gptr_free_rcu(struct rtw_wds_gptr_table *tbl, struct rtw_wds_gptr *wgptr)
|
||||
{
|
||||
_adapter *adapter = wgptr->adapter;
|
||||
|
||||
ATOMIC_DEC(&adapter->wds_gpt_record_num);
|
||||
|
||||
rtw_wgptr_free_rcu(wgptr);
|
||||
}
|
||||
|
||||
static u32 rtw_wds_gptr_table_hash(const void *addr, u32 len, u32 seed)
|
||||
{
|
||||
/* Use last four bytes of hw addr as hash index */
|
||||
return jhash_1word(*(u32 *)(addr+2), seed);
|
||||
}
|
||||
|
||||
static const rtw_rhashtable_params rtw_wds_gptr_rht_params = {
|
||||
.nelem_hint = 2,
|
||||
.automatic_shrinking = true,
|
||||
.key_len = ETH_ALEN,
|
||||
.key_offset = offsetof(struct rtw_wds_gptr, src),
|
||||
.head_offset = offsetof(struct rtw_wds_gptr, rhash),
|
||||
.hashfn = rtw_wds_gptr_table_hash,
|
||||
};
|
||||
|
||||
static void rtw_wds_gptr_rht_free(void *ptr, void *tblptr)
|
||||
{
|
||||
struct rtw_wds_gptr *wgptr = ptr;
|
||||
struct rtw_wds_gptr_table *tbl = tblptr;
|
||||
|
||||
rtw_wds_gptr_free_rcu(tbl, wgptr);
|
||||
}
|
||||
|
||||
static struct rtw_wds_gptr_table *rtw_wds_gptr_table_alloc(void)
|
||||
{
|
||||
struct rtw_wds_gptr_table *newtbl;
|
||||
|
||||
newtbl = rtw_malloc(sizeof(struct rtw_wds_gptr_table));
|
||||
if (!newtbl)
|
||||
return NULL;
|
||||
|
||||
return newtbl;
|
||||
}
|
||||
|
||||
static void rtw_wds_gptr_table_free(struct rtw_wds_gptr_table *tbl)
|
||||
{
|
||||
rtw_rhashtable_free_and_destroy(&tbl->rhead,
|
||||
rtw_wds_gptr_rht_free, tbl);
|
||||
rtw_mfree(tbl, sizeof(struct rtw_wds_gptr_table));
|
||||
}
|
||||
|
||||
static struct rtw_wds_gptr *rtw_wds_gptr_lookup(_adapter *adapter, const u8 *src)
|
||||
{
|
||||
struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
|
||||
|
||||
if (!tbl)
|
||||
return NULL;
|
||||
|
||||
return rtw_rhashtable_lookup_fast(&tbl->rhead, src, rtw_wds_gptr_rht_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locking: must be called within a read rcu section.
|
||||
*/
|
||||
static struct rtw_wds_gptr *rtw_wds_gptr_lookup_by_idx(_adapter *adapter, int idx)
|
||||
{
|
||||
int i = 0, ret;
|
||||
struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
|
||||
struct rtw_wds_gptr *wgptr = NULL;
|
||||
rtw_rhashtable_iter iter;
|
||||
|
||||
if (!tbl)
|
||||
return NULL;
|
||||
|
||||
ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
ret = rtw_rhashtable_walk_start(&iter);
|
||||
if (ret && ret != -EAGAIN)
|
||||
goto err;
|
||||
|
||||
while ((wgptr = rtw_rhashtable_walk_next(&iter))) {
|
||||
if (IS_ERR(wgptr) && PTR_ERR(wgptr) == -EAGAIN)
|
||||
continue;
|
||||
if (IS_ERR(wgptr))
|
||||
break;
|
||||
if (i++ == idx)
|
||||
break;
|
||||
}
|
||||
err:
|
||||
rtw_rhashtable_walk_stop(&iter);
|
||||
rtw_rhashtable_walk_exit(&iter);
|
||||
|
||||
if (IS_ERR(wgptr) || !wgptr)
|
||||
return NULL;
|
||||
|
||||
return wgptr;
|
||||
}
|
||||
|
||||
void dump_wgptr(void *sel, _adapter *adapter)
|
||||
{
|
||||
struct rtw_wds_gptr *wgptr;
|
||||
int idx = 0;
|
||||
char src[ETH_ALEN];
|
||||
u32 age_ms;
|
||||
|
||||
RTW_PRINT_SEL(sel, "num:%d\n", ATOMIC_READ(&adapter->wds_gpt_record_num));
|
||||
RTW_PRINT_SEL(sel, "%-17s %-6s\n"
|
||||
, "src", "age"
|
||||
);
|
||||
|
||||
do {
|
||||
rtw_rcu_read_lock();
|
||||
|
||||
wgptr = rtw_wds_gptr_lookup_by_idx(adapter, idx);
|
||||
if (wgptr) {
|
||||
_rtw_memcpy(src, wgptr->src, ETH_ALEN);
|
||||
age_ms = rtw_get_passing_time_ms(wgptr->last_update);
|
||||
}
|
||||
|
||||
rtw_rcu_read_unlock();
|
||||
|
||||
if (wgptr) {
|
||||
RTW_PRINT_SEL(sel, MAC_FMT" %6u\n"
|
||||
, MAC_ARG(src)
|
||||
, age_ms < 999999 ? age_ms : 999999
|
||||
);
|
||||
}
|
||||
|
||||
idx++;
|
||||
} while (wgptr);
|
||||
}
|
||||
|
||||
static struct rtw_wds_gptr *rtw_wds_gptr_new(_adapter *adapter, const u8 *src)
|
||||
{
|
||||
struct rtw_wds_gptr *new_wgptr;
|
||||
|
||||
new_wgptr = rtw_zmalloc(sizeof(struct rtw_wds_gptr));
|
||||
if (!new_wgptr)
|
||||
return NULL;
|
||||
|
||||
new_wgptr->adapter = adapter;
|
||||
_rtw_memcpy(new_wgptr->src, src, ETH_ALEN);
|
||||
new_wgptr->last_update = rtw_get_current_time();
|
||||
|
||||
return new_wgptr;
|
||||
}
|
||||
|
||||
static struct rtw_wds_gptr *rtw_wds_gptr_add(_adapter *adapter, const u8 *src)
|
||||
{
|
||||
struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
|
||||
struct rtw_wds_gptr *wgptr, *new_wgptr;
|
||||
int ret;
|
||||
|
||||
if (!tbl)
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
|
||||
if (ATOMIC_INC_UNLESS(&adapter->wds_gpt_record_num, RTW_WDS_MAX_PATHS) == 0)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
|
||||
new_wgptr = rtw_wds_gptr_new(adapter, src);
|
||||
if (!new_wgptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
do {
|
||||
ret = rtw_rhashtable_lookup_insert_fast(&tbl->rhead,
|
||||
&new_wgptr->rhash,
|
||||
rtw_wds_gptr_rht_params);
|
||||
|
||||
if (ret == -EEXIST)
|
||||
wgptr = rtw_rhashtable_lookup_fast(&tbl->rhead,
|
||||
src,
|
||||
rtw_wds_gptr_rht_params);
|
||||
|
||||
} while (unlikely(ret == -EEXIST && !wgptr));
|
||||
|
||||
if (ret && ret != -EEXIST)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/* At this point either new_wgptr was added, or we found a
|
||||
* matching entry already in the table; in the latter case
|
||||
* free the unnecessary new entry.
|
||||
*/
|
||||
if (ret == -EEXIST) {
|
||||
rtw_mfree(new_wgptr, sizeof(struct rtw_wds_gptr));
|
||||
new_wgptr = wgptr;
|
||||
}
|
||||
|
||||
return new_wgptr;
|
||||
}
|
||||
|
||||
bool rtw_rx_wds_gptr_check(_adapter *adapter, const u8 *src)
|
||||
{
|
||||
struct rtw_wds_gptr *wgptr;
|
||||
bool ret = 0;
|
||||
|
||||
rtw_rcu_read_lock();
|
||||
|
||||
wgptr = rtw_wds_gptr_lookup(adapter, src);
|
||||
if (wgptr)
|
||||
ret = rtw_time_after(wgptr->last_update + RTW_WDS_GPTR_EXPIRE, rtw_get_current_time());
|
||||
|
||||
rtw_rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rtw_tx_wds_gptr_update(_adapter *adapter, const u8 *src)
|
||||
{
|
||||
struct rtw_wds_gptr *wgptr;
|
||||
|
||||
rtw_rcu_read_lock();
|
||||
wgptr = rtw_wds_gptr_lookup(adapter, src);
|
||||
if (!wgptr)
|
||||
rtw_wds_gptr_add(adapter, src);
|
||||
else
|
||||
wgptr->last_update = rtw_get_current_time();
|
||||
rtw_rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void __rtw_wds_gptr_del(struct rtw_wds_gptr_table *tbl, struct rtw_wds_gptr *wgptr)
|
||||
{
|
||||
rtw_rhashtable_remove_fast(&tbl->rhead, &wgptr->rhash, rtw_wds_gptr_rht_params);
|
||||
rtw_wds_gptr_free_rcu(tbl, wgptr);
|
||||
}
|
||||
|
||||
void rtw_wds_gptr_expire(_adapter *adapter)
|
||||
{
|
||||
struct rtw_wds_gptr_table *tbl = adapter->wds_gpt_records;
|
||||
struct rtw_wds_gptr *wgptr;
|
||||
rtw_rhashtable_iter iter;
|
||||
int ret;
|
||||
|
||||
if (!tbl)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_enter(&tbl->rhead, &iter);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = rtw_rhashtable_walk_start(&iter);
|
||||
if (ret && ret != -EAGAIN)
|
||||
goto out;
|
||||
|
||||
while ((wgptr = rtw_rhashtable_walk_next(&iter))) {
|
||||
if (IS_ERR(wgptr) && PTR_ERR(wgptr) == -EAGAIN)
|
||||
continue;
|
||||
if (IS_ERR(wgptr))
|
||||
break;
|
||||
if (rtw_time_after(rtw_get_current_time(), wgptr->last_update + RTW_WDS_GPTR_EXPIRE))
|
||||
__rtw_wds_gptr_del(tbl, wgptr);
|
||||
}
|
||||
|
||||
out:
|
||||
rtw_rhashtable_walk_stop(&iter);
|
||||
rtw_rhashtable_walk_exit(&iter);
|
||||
}
|
||||
|
||||
int rtw_wds_gptr_tbl_init(_adapter *adapter)
|
||||
{
|
||||
struct rtw_wds_gptr_table *tbl;
|
||||
int ret;
|
||||
|
||||
tbl = rtw_wds_gptr_table_alloc();
|
||||
if (!tbl)
|
||||
return -ENOMEM;
|
||||
|
||||
rtw_rhashtable_init(&tbl->rhead, &rtw_wds_gptr_rht_params);
|
||||
|
||||
ATOMIC_SET(&adapter->wds_gpt_record_num, 0);
|
||||
adapter->wds_gpt_records = tbl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtw_wds_gptr_tbl_unregister(_adapter *adapter)
|
||||
{
|
||||
if (adapter->wds_gpt_records) {
|
||||
rtw_wds_gptr_table_free(adapter->wds_gpt_records);
|
||||
adapter->wds_gpt_records = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* defined(CONFIG_RTW_WDS) */
|
||||
|
||||
65
core/wds/rtw_wds.h
Normal file
65
core/wds/rtw_wds.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright(c) 2007 - 2019 Realtek Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*****************************************************************************/
|
||||
#ifndef __RTW_WDS_H_
|
||||
#define __RTW_WDS_H_
|
||||
|
||||
#ifdef CONFIG_AP_MODE
|
||||
struct rtw_wds_path {
|
||||
u8 dst[ETH_ALEN];
|
||||
rtw_rhash_head rhash;
|
||||
_adapter *adapter;
|
||||
struct sta_info __rcu *next_hop;
|
||||
rtw_rcu_head rcu;
|
||||
systime last_update;
|
||||
};
|
||||
|
||||
struct rtw_wds_table {
|
||||
rtw_rhashtable rhead;
|
||||
};
|
||||
|
||||
#define RTW_WDS_PATH_EXPIRE (600 * HZ)
|
||||
|
||||
/* Maximum number of paths per interface */
|
||||
#define RTW_WDS_MAX_PATHS 1024
|
||||
|
||||
int rtw_wds_nexthop_lookup(_adapter *adapter, const u8 *da, u8 *ra);
|
||||
|
||||
struct rtw_wds_path *rtw_wds_path_lookup(_adapter *adapter, const u8 *dst);
|
||||
void dump_wpath(void *sel, _adapter *adapter);
|
||||
|
||||
void rtw_wds_path_expire(_adapter *adapter);
|
||||
|
||||
struct rtw_wds_path *rtw_wds_path_add(_adapter *adapter, const u8 *dst, struct sta_info *next_hop);
|
||||
void rtw_wds_path_assign_nexthop(struct rtw_wds_path *path, struct sta_info *sta);
|
||||
|
||||
int rtw_wds_pathtbl_init(_adapter *adapter);
|
||||
void rtw_wds_pathtbl_unregister(_adapter *adapter);
|
||||
|
||||
void rtw_wds_path_flush_by_nexthop(struct sta_info *sta);
|
||||
#endif /* CONFIG_AP_MODE */
|
||||
|
||||
struct rtw_wds_gptr_table {
|
||||
rtw_rhashtable rhead;
|
||||
};
|
||||
|
||||
void dump_wgptr(void *sel, _adapter *adapter);
|
||||
bool rtw_rx_wds_gptr_check(_adapter *adapter, const u8 *src);
|
||||
void rtw_tx_wds_gptr_update(_adapter *adapter, const u8 *src);
|
||||
void rtw_wds_gptr_expire(_adapter *adapter);
|
||||
int rtw_wds_gptr_tbl_init(_adapter *adapter);
|
||||
void rtw_wds_gptr_tbl_unregister(_adapter *adapter);
|
||||
|
||||
#endif /* __RTW_WDSH_ */
|
||||
|
||||
Reference in New Issue
Block a user