madwifi: fix the long standing bug that is triggered by nodes getting a timeout on one vap, then moving to another
SVN-Revision: 14171
This commit is contained in:
parent
28abf79c44
commit
87db54da68
6 changed files with 843 additions and 3 deletions
29
package/madwifi/patches/391-vap_auth.patch
Normal file
29
package/madwifi/patches/391-vap_auth.patch
Normal file
|
@ -0,0 +1,29 @@
|
|||
--- a/net80211/ieee80211_input.c
|
||||
+++ b/net80211/ieee80211_input.c
|
||||
@@ -1374,7 +1386,7 @@ ieee80211_auth_open(struct ieee80211_nod
|
||||
vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
|
||||
if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
|
||||
if (ni == vap->iv_bss) {
|
||||
- ni = ieee80211_dup_bss(vap, wh->i_addr2, 0);
|
||||
+ ni = ieee80211_dup_bss(vap, wh->i_addr2, 1);
|
||||
if (ni == NULL)
|
||||
return;
|
||||
tmpnode = 1;
|
||||
@@ -1762,6 +1774,8 @@ ieee80211_ssid_mismatch(struct ieee80211
|
||||
}
|
||||
|
||||
#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
|
||||
+ if ((_ni)->ni_esslen == 0) \
|
||||
+ return; \
|
||||
if ((_ssid)[1] != 0 && \
|
||||
((_ssid)[1] != (_ni)->ni_esslen || \
|
||||
memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
|
||||
@@ -1776,6 +1790,8 @@ ieee80211_ssid_mismatch(struct ieee80211
|
||||
} while (0)
|
||||
#else /* !IEEE80211_DEBUG */
|
||||
#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
|
||||
+ if ((_ni)->ni_esslen == 0) \
|
||||
+ return; \
|
||||
if ((_ssid)[1] != 0 && \
|
||||
((_ssid)[1] != (_ni)->ni_esslen || \
|
||||
memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
|
388
package/madwifi/patches/392-remove_wds_nodetracking.patch
Normal file
388
package/madwifi/patches/392-remove_wds_nodetracking.patch
Normal file
|
@ -0,0 +1,388 @@
|
|||
--- a/net80211/ieee80211_input.c
|
||||
+++ b/net80211/ieee80211_input.c
|
||||
@@ -572,36 +572,6 @@ ieee80211_input(struct ieee80211vap * va
|
||||
}
|
||||
}
|
||||
|
||||
- /* XXX: Useless node mgmt API; make better */
|
||||
- if ((dir == IEEE80211_FC1_DIR_DSTODS) && !vap->iv_wdsnode &&
|
||||
- !ni_wds && !ni->ni_subif) {
|
||||
- struct ieee80211_node_table *nt = &ic->ic_sta;
|
||||
- struct ieee80211_frame_addr4 *wh4;
|
||||
-
|
||||
- if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) {
|
||||
- IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
|
||||
- wh, "data", "%s", "4 addr not allowed");
|
||||
- goto err;
|
||||
- }
|
||||
- wh4 = (struct ieee80211_frame_addr4 *)skb->data;
|
||||
- ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4);
|
||||
- /* Last call increments ref count if !NULL */
|
||||
- if ((ni_wds != NULL) && (ni_wds != ni)) {
|
||||
- /*
|
||||
- * node with source address (addr4) moved
|
||||
- * to another WDS capable station. remove the
|
||||
- * reference to the previous station and add
|
||||
- * reference to the new one
|
||||
- */
|
||||
- (void) ieee80211_remove_wds_addr(nt, wh4->i_addr4);
|
||||
- ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0);
|
||||
- }
|
||||
- if (ni_wds == NULL)
|
||||
- ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0);
|
||||
- else
|
||||
- ieee80211_unref_node(&ni_wds);
|
||||
- }
|
||||
-
|
||||
/*
|
||||
* Check for power save state change.
|
||||
*/
|
||||
--- a/net80211/ieee80211_node.c
|
||||
+++ b/net80211/ieee80211_node.c
|
||||
@@ -122,7 +122,6 @@ static void ieee80211_node_table_init(st
|
||||
static void ieee80211_node_table_cleanup(struct ieee80211_node_table *);
|
||||
static void ieee80211_node_table_reset(struct ieee80211_node_table *,
|
||||
struct ieee80211vap *);
|
||||
-static void ieee80211_node_wds_ageout(unsigned long);
|
||||
|
||||
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
|
||||
|
||||
@@ -785,10 +784,6 @@ ieee80211_node_table_init(struct ieee802
|
||||
nt->nt_name = name;
|
||||
nt->nt_scangen = 1;
|
||||
nt->nt_inact_init = inact;
|
||||
- init_timer(&nt->nt_wds_aging_timer);
|
||||
- nt->nt_wds_aging_timer.function = ieee80211_node_wds_ageout;
|
||||
- nt->nt_wds_aging_timer.data = (unsigned long) nt;
|
||||
- mod_timer(&nt->nt_wds_aging_timer, jiffies + HZ * WDS_AGING_TIMER_VAL);
|
||||
}
|
||||
|
||||
static __inline
|
||||
@@ -1201,142 +1196,6 @@ void ieee80211_wds_addif(struct ieee8021
|
||||
schedule_work(&ni->ni_create);
|
||||
}
|
||||
|
||||
-/* Add wds address to the node table */
|
||||
-int
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-ieee80211_add_wds_addr_debug(struct ieee80211_node_table *nt,
|
||||
- struct ieee80211_node *ni, const u_int8_t *macaddr, u_int8_t wds_static,
|
||||
- const char* func, int line)
|
||||
-#else
|
||||
-ieee80211_add_wds_addr(struct ieee80211_node_table *nt,
|
||||
- struct ieee80211_node *ni, const u_int8_t *macaddr, u_int8_t wds_static)
|
||||
-#endif
|
||||
-{
|
||||
- int hash;
|
||||
- struct ieee80211_wds_addr *wds;
|
||||
-
|
||||
- MALLOC(wds, struct ieee80211_wds_addr *, sizeof(struct ieee80211_wds_addr),
|
||||
- M_80211_WDS, M_NOWAIT | M_ZERO);
|
||||
- if (wds == NULL) {
|
||||
- /* XXX msg */
|
||||
- return 1;
|
||||
- }
|
||||
- if (wds_static)
|
||||
- wds->wds_agingcount = WDS_AGING_STATIC;
|
||||
- else
|
||||
- wds->wds_agingcount = WDS_AGING_COUNT;
|
||||
- hash = IEEE80211_NODE_HASH(macaddr);
|
||||
- IEEE80211_ADDR_COPY(wds->wds_macaddr, macaddr);
|
||||
-
|
||||
- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- wds->wds_ni = ieee80211_ref_node_debug(ni, func, line);
|
||||
-#else
|
||||
- wds->wds_ni = ieee80211_ref_node(ni);
|
||||
-#endif
|
||||
- LIST_INSERT_HEAD(&nt->nt_wds_hash[hash], wds, wds_hash);
|
||||
- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
- return 0;
|
||||
-}
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-EXPORT_SYMBOL(ieee80211_add_wds_addr_debug);
|
||||
-#else
|
||||
-EXPORT_SYMBOL(ieee80211_add_wds_addr);
|
||||
-#endif
|
||||
-
|
||||
-/* remove wds address from the wds hash table */
|
||||
-void
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-ieee80211_remove_wds_addr_debug(struct ieee80211_node_table *nt, const u_int8_t *macaddr,
|
||||
- const char* func, int line)
|
||||
-#else
|
||||
-ieee80211_remove_wds_addr(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
|
||||
-#endif
|
||||
-{
|
||||
- int hash;
|
||||
- struct ieee80211_wds_addr *wds, *twds;
|
||||
-
|
||||
- hash = IEEE80211_NODE_HASH(macaddr);
|
||||
- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
- LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
|
||||
- if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
|
||||
- LIST_REMOVE(wds, wds_hash);
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- ieee80211_unref_node_debug(&wds->wds_ni, func, line);
|
||||
-#else
|
||||
- ieee80211_unref_node(&wds->wds_ni);
|
||||
-#endif
|
||||
- FREE(wds, M_80211_WDS);
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
-}
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-EXPORT_SYMBOL(ieee80211_remove_wds_addr_debug);
|
||||
-#else
|
||||
-EXPORT_SYMBOL(ieee80211_remove_wds_addr);
|
||||
-#endif
|
||||
-
|
||||
-/* Remove node references from wds table */
|
||||
-void
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-ieee80211_del_wds_node_debug(struct ieee80211_node_table *nt, struct ieee80211_node *ni,
|
||||
- const char* func, int line)
|
||||
-#else
|
||||
-ieee80211_del_wds_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
|
||||
-#endif
|
||||
-{
|
||||
- int hash;
|
||||
- struct ieee80211_wds_addr *wds, *twds;
|
||||
-
|
||||
- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
- for (hash = 0; hash < IEEE80211_NODE_HASHSIZE; hash++) {
|
||||
- LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
|
||||
- if (wds->wds_ni == ni) {
|
||||
- LIST_REMOVE(wds, wds_hash);
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- ieee80211_unref_node_debug(&wds->wds_ni, func, line);
|
||||
-#else
|
||||
- ieee80211_unref_node(&wds->wds_ni);
|
||||
-#endif
|
||||
- FREE(wds, M_80211_WDS);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
-}
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-EXPORT_SYMBOL(ieee80211_del_wds_node_debug);
|
||||
-#else
|
||||
-EXPORT_SYMBOL(ieee80211_del_wds_node);
|
||||
-#endif
|
||||
-
|
||||
-static void
|
||||
-ieee80211_node_wds_ageout(unsigned long data)
|
||||
-{
|
||||
- struct ieee80211_node_table *nt = (struct ieee80211_node_table *)data;
|
||||
- int hash;
|
||||
- struct ieee80211_wds_addr *wds, *twds;
|
||||
-
|
||||
- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
- for (hash = 0; hash < IEEE80211_NODE_HASHSIZE; hash++) {
|
||||
- LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
|
||||
- if (wds->wds_agingcount != WDS_AGING_STATIC) {
|
||||
- if (!wds->wds_agingcount) {
|
||||
- LIST_REMOVE(wds, wds_hash);
|
||||
- ieee80211_unref_node(&wds->wds_ni);
|
||||
- FREE(wds, M_80211_WDS);
|
||||
- } else
|
||||
- wds->wds_agingcount--;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
- mod_timer(&nt->nt_wds_aging_timer, jiffies + HZ * WDS_AGING_TIMER_VAL);
|
||||
-}
|
||||
-
|
||||
-
|
||||
/* Add the specified station to the station table.
|
||||
* Allocates a new ieee80211_node* that has a reference count of one
|
||||
* If tmp is 0, it is added to the node table and the reference is used.
|
||||
@@ -1382,34 +1241,6 @@ ieee80211_dup_bss(struct ieee80211vap *v
|
||||
return ni;
|
||||
}
|
||||
|
||||
-static struct ieee80211_node *
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-ieee80211_find_wds_node_locked_debug(struct ieee80211_node_table *nt,
|
||||
- const u_int8_t *macaddr, const char* func, int line)
|
||||
-#else
|
||||
-ieee80211_find_wds_node_locked(struct ieee80211_node_table *nt,
|
||||
- const u_int8_t *macaddr)
|
||||
-#endif
|
||||
-{
|
||||
- struct ieee80211_wds_addr *wds;
|
||||
- int hash;
|
||||
- IEEE80211_NODE_TABLE_LOCK_ASSERT(nt);
|
||||
-
|
||||
- hash = IEEE80211_NODE_HASH(macaddr);
|
||||
- LIST_FOREACH(wds, &nt->nt_wds_hash[hash], wds_hash) {
|
||||
- if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
|
||||
- if (wds->wds_agingcount != WDS_AGING_STATIC)
|
||||
- wds->wds_agingcount = WDS_AGING_COUNT; /* reset the aging count */
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- return ieee80211_ref_node_debug(wds->wds_ni, func, line);
|
||||
-#else
|
||||
- return ieee80211_ref_node(wds->wds_ni);
|
||||
-#endif
|
||||
- }
|
||||
- }
|
||||
- return NULL;
|
||||
-}
|
||||
-
|
||||
/* NB: A node reference is acquired here; the caller MUST release it. */
|
||||
#ifdef IEEE80211_DEBUG_REFCNT
|
||||
#define ieee80211_find_node_locked(nt, mac) \
|
||||
@@ -1427,7 +1258,6 @@ ieee80211_find_node_locked(struct ieee80
|
||||
{
|
||||
struct ieee80211_node *ni;
|
||||
int hash;
|
||||
- struct ieee80211_wds_addr *wds;
|
||||
|
||||
IEEE80211_NODE_TABLE_LOCK_ASSERT(nt);
|
||||
|
||||
@@ -1442,48 +1272,11 @@ ieee80211_find_node_locked(struct ieee80
|
||||
return ni;
|
||||
}
|
||||
}
|
||||
-
|
||||
- /* Now, we look for the desired mac address in the 4 address
|
||||
- nodes. */
|
||||
- LIST_FOREACH(wds, &nt->nt_wds_hash[hash], wds_hash) {
|
||||
- if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- return ieee80211_ref_node_debug(wds->wds_ni, func, line);
|
||||
-#else
|
||||
- return ieee80211_ref_node(wds->wds_ni);
|
||||
-#endif
|
||||
- }
|
||||
- }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ieee80211_node *
|
||||
#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-ieee80211_find_wds_node_debug(struct ieee80211_node_table *nt, const u_int8_t *macaddr,
|
||||
- const char* func, int line)
|
||||
-#else
|
||||
-ieee80211_find_wds_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
|
||||
-#endif
|
||||
-{
|
||||
- struct ieee80211_node *ni;
|
||||
-
|
||||
- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- ni = ieee80211_find_wds_node_locked_debug(nt, macaddr, func, line);
|
||||
-#else
|
||||
- ni = ieee80211_find_wds_node_locked(nt, macaddr);
|
||||
-#endif
|
||||
- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
- return ni;
|
||||
-}
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-EXPORT_SYMBOL(ieee80211_find_wds_node_debug);
|
||||
-#else
|
||||
-EXPORT_SYMBOL(ieee80211_find_wds_node);
|
||||
-#endif
|
||||
-
|
||||
-struct ieee80211_node *
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
ieee80211_find_node_debug(struct ieee80211_node_table *nt,
|
||||
const u_int8_t *macaddr, const char *func, int line)
|
||||
#else
|
||||
@@ -1835,7 +1628,6 @@ ieee80211_node_table_cleanup(struct ieee
|
||||
ic->ic_node_cleanup(ni);
|
||||
#endif
|
||||
}
|
||||
- del_timer(&nt->nt_wds_aging_timer);
|
||||
IEEE80211_SCAN_LOCK_DESTROY(nt);
|
||||
IEEE80211_NODE_TABLE_LOCK_DESTROY(nt);
|
||||
}
|
||||
@@ -2402,8 +2194,6 @@ ieee80211_node_leave(struct ieee80211_no
|
||||
* so no more references are generated
|
||||
*/
|
||||
if (nt) {
|
||||
- ieee80211_remove_wds_addr(nt, ni->ni_macaddr);
|
||||
- ieee80211_del_wds_node(nt, ni);
|
||||
IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
node_table_leave_locked(nt, ni);
|
||||
IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
--- a/net80211/ieee80211_node.h
|
||||
+++ b/net80211/ieee80211_node.h
|
||||
@@ -231,13 +231,6 @@ void ieee80211_sta_leave(struct ieee8021
|
||||
#define WDS_AGING_STATIC 0xffff
|
||||
#define WDS_AGING_TIMER_VAL (WDS_AGING_TIME / 2)
|
||||
|
||||
-struct ieee80211_wds_addr {
|
||||
- LIST_ENTRY(ieee80211_wds_addr) wds_hash;
|
||||
- u_int8_t wds_macaddr[IEEE80211_ADDR_LEN];
|
||||
- struct ieee80211_node *wds_ni;
|
||||
- u_int16_t wds_agingcount;
|
||||
-};
|
||||
-
|
||||
/*
|
||||
* Table of ieee80211_node instances. Each ieee80211com
|
||||
* has at least one for holding the scan candidates.
|
||||
@@ -250,11 +243,9 @@ struct ieee80211_node_table {
|
||||
ieee80211_node_table_lock_t nt_nodelock; /* on node table */
|
||||
TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */
|
||||
ATH_LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
|
||||
- ATH_LIST_HEAD(, ieee80211_wds_addr) nt_wds_hash[IEEE80211_NODE_HASHSIZE];
|
||||
ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */
|
||||
u_int nt_scangen; /* gen# for timeout scan */
|
||||
int nt_inact_init; /* initial node inact setting */
|
||||
- struct timer_list nt_wds_aging_timer; /* timer to age out wds entries */
|
||||
};
|
||||
|
||||
/* Allocates a new ieee80211_node* that has a reference count of one, and
|
||||
@@ -363,47 +354,6 @@ void
|
||||
ieee80211_unref_node(struct ieee80211_node **pni);
|
||||
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
|
||||
|
||||
-/* Increments reference count of ieee80211_node *ni */
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-#define ieee80211_add_wds_addr(_table, _node, _mac, _static) \
|
||||
- ieee80211_add_wds_addr_debug(_table, _node, _mac, _static, __func__, __LINE__)
|
||||
-int ieee80211_add_wds_addr_debug(struct ieee80211_node_table *, struct ieee80211_node *,
|
||||
- const u_int8_t *, u_int8_t, const char* func, int line);
|
||||
-#else
|
||||
-int ieee80211_add_wds_addr(struct ieee80211_node_table *, struct ieee80211_node *,
|
||||
- const u_int8_t *, u_int8_t);
|
||||
-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
|
||||
-
|
||||
-/* Decrements reference count of ieee80211_node *ni */
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-#define ieee80211_remove_wds_addr(_table, _mac) \
|
||||
- ieee80211_remove_wds_addr_debug(_table, _mac, __func__, __LINE__)
|
||||
-void ieee80211_remove_wds_addr_debug(struct ieee80211_node_table *, const u_int8_t *,
|
||||
- const char* func, int line);
|
||||
-#else
|
||||
-void ieee80211_remove_wds_addr(struct ieee80211_node_table *, const u_int8_t *);
|
||||
-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
|
||||
-
|
||||
-/* Decrements reference count of node, if found */
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-#define ieee80211_del_wds_node(_table, _node) \
|
||||
- ieee80211_del_wds_node_debug(_table, _node, __func__, __LINE__)
|
||||
-void ieee80211_del_wds_node_debug(struct ieee80211_node_table *, struct ieee80211_node *,
|
||||
- const char* func, int line);
|
||||
-#else
|
||||
-void ieee80211_del_wds_node(struct ieee80211_node_table *, struct ieee80211_node *);
|
||||
-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
|
||||
-
|
||||
-/* Increments reference count of node, if found */
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-#define ieee80211_find_wds_node(_table, _mac) \
|
||||
- ieee80211_find_wds_node_debug(_table, _mac, __func__, __LINE__)
|
||||
-struct ieee80211_node *ieee80211_find_wds_node_debug(struct ieee80211_node_table *,
|
||||
- const u_int8_t *, const char* func, int line);
|
||||
-#else
|
||||
-struct ieee80211_node *ieee80211_find_wds_node(struct ieee80211_node_table *,
|
||||
- const u_int8_t *);
|
||||
-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
|
||||
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
|
||||
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
|
||||
ieee80211_iter_func *, void *);
|
423
package/madwifi/patches/393-mbss_vap_auth.patch
Normal file
423
package/madwifi/patches/393-mbss_vap_auth.patch
Normal file
|
@ -0,0 +1,423 @@
|
|||
--- a/net80211/ieee80211_node.c
|
||||
+++ b/net80211/ieee80211_node.c
|
||||
@@ -123,6 +123,9 @@ static void ieee80211_node_table_cleanup
|
||||
static void ieee80211_node_table_reset(struct ieee80211_node_table *,
|
||||
struct ieee80211vap *);
|
||||
|
||||
+static struct ieee80211_node *
|
||||
+lookup_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap, const u_int8_t *addr);
|
||||
+
|
||||
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
|
||||
|
||||
void
|
||||
@@ -697,7 +700,7 @@ ieee80211_sta_join(struct ieee80211vap *
|
||||
struct ieee80211com *ic = vap->iv_ic;
|
||||
struct ieee80211_node *ni;
|
||||
|
||||
- ni = ieee80211_find_node(&ic->ic_sta, se->se_macaddr);
|
||||
+ ni = lookup_rxnode(ic, vap, se->se_macaddr);
|
||||
if (ni == NULL) {
|
||||
ni = ieee80211_alloc_node_table(vap, se->se_macaddr);
|
||||
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
|
||||
@@ -1391,6 +1394,53 @@ ieee80211_add_neighbor(struct ieee80211v
|
||||
return ni;
|
||||
}
|
||||
|
||||
+struct ieee80211vap *
|
||||
+ieee80211_find_rxvap(struct ieee80211com *ic, const u_int8_t *mac)
|
||||
+{
|
||||
+ struct ieee80211vap *vap;
|
||||
+
|
||||
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
|
||||
+ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, mac))
|
||||
+ return vap;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_find_rxvap);
|
||||
+
|
||||
+static struct ieee80211_node *
|
||||
+lookup_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap,
|
||||
+ const u_int8_t *addr)
|
||||
+{
|
||||
+ struct ieee80211_node_table *nt;
|
||||
+ struct ieee80211_node *ni = NULL;
|
||||
+ int use_bss = 0;
|
||||
+ int hash;
|
||||
+
|
||||
+ nt = &ic->ic_sta;
|
||||
+ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
+ hash = IEEE80211_NODE_HASH(addr);
|
||||
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
|
||||
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, addr)) {
|
||||
+ /* allow multiple nodes on different vaps */
|
||||
+ if (vap && (ni->ni_vap != vap))
|
||||
+ continue;
|
||||
+
|
||||
+ ieee80211_ref_node(ni);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* no match found */
|
||||
+ ni = NULL;
|
||||
+
|
||||
+out:
|
||||
+ IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
+ return ni;
|
||||
+#undef IS_PSPOLL
|
||||
+#undef IS_CTL
|
||||
+}
|
||||
+
|
||||
+
|
||||
/*
|
||||
* Return the node for the sender of a frame; if the sender is unknown return
|
||||
* NULL. The caller is expected to deal with this. (The frame is sent to all
|
||||
@@ -1400,10 +1450,10 @@ ieee80211_add_neighbor(struct ieee80211v
|
||||
*/
|
||||
struct ieee80211_node *
|
||||
#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-ieee80211_find_rxnode_debug(struct ieee80211com *ic,
|
||||
+ieee80211_find_rxnode_debug(struct ieee80211com *ic, struct ieee80211vap *vap,
|
||||
const struct ieee80211_frame_min *wh, const char *func, int line)
|
||||
#else
|
||||
-ieee80211_find_rxnode(struct ieee80211com *ic,
|
||||
+ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap,
|
||||
const struct ieee80211_frame_min *wh)
|
||||
#endif
|
||||
{
|
||||
@@ -1411,9 +1461,8 @@ ieee80211_find_rxnode(struct ieee80211co
|
||||
((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
|
||||
#define IS_PSPOLL(wh) \
|
||||
((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
|
||||
- struct ieee80211_node_table *nt;
|
||||
- struct ieee80211_node *ni;
|
||||
- struct ieee80211vap *vap, *avp;
|
||||
+ struct ieee80211_node *ni = NULL;
|
||||
+ struct ieee80211vap *avp;
|
||||
const u_int8_t *addr;
|
||||
|
||||
if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
|
||||
@@ -1426,32 +1475,21 @@ ieee80211_find_rxnode(struct ieee80211co
|
||||
|
||||
/* XXX check ic_bss first in station mode */
|
||||
/* XXX 4-address frames? */
|
||||
- nt = &ic->ic_sta;
|
||||
- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) {
|
||||
- TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
|
||||
+ if (vap) { /* assume unicast if vap is set, mcast not supported for wds */
|
||||
TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
|
||||
- if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac))
|
||||
+ if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac) ||
|
||||
+ !IEEE80211_ADDR_EQ(wh->i_addr1, avp->iv_myaddr))
|
||||
continue;
|
||||
|
||||
if (avp->iv_wdsnode)
|
||||
- return ieee80211_ref_node(avp->iv_wdsnode);
|
||||
- else
|
||||
- return NULL;
|
||||
+ ni = ieee80211_ref_node(avp->iv_wdsnode);
|
||||
}
|
||||
}
|
||||
+ return ni;
|
||||
}
|
||||
|
||||
-#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- ni = ieee80211_find_node_locked_debug(nt, addr, func, line);
|
||||
-#else
|
||||
- ni = ieee80211_find_node_locked(nt, addr);
|
||||
-#endif
|
||||
- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
-
|
||||
- return ni;
|
||||
-#undef IS_PSPOLL
|
||||
-#undef IS_CTL
|
||||
+ return lookup_rxnode(ic, vap, addr);
|
||||
}
|
||||
#ifdef IEEE80211_DEBUG_REFCNT
|
||||
EXPORT_SYMBOL(ieee80211_find_rxnode_debug);
|
||||
@@ -1476,15 +1514,14 @@ ieee80211_find_txnode(struct ieee80211va
|
||||
struct ieee80211com *ic = vap->iv_ic;
|
||||
struct ieee80211_node_table *nt;
|
||||
struct ieee80211_node *ni = NULL;
|
||||
+ int hash;
|
||||
|
||||
- IEEE80211_LOCK_IRQ(ic);
|
||||
if (vap->iv_opmode == IEEE80211_M_WDS) {
|
||||
if (vap->iv_wdsnode && (vap->iv_state == IEEE80211_S_RUN))
|
||||
return ieee80211_ref_node(vap->iv_wdsnode);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
- IEEE80211_UNLOCK_IRQ(ic);
|
||||
|
||||
/*
|
||||
* The destination address should be in the node table
|
||||
@@ -1502,11 +1539,22 @@ ieee80211_find_txnode(struct ieee80211va
|
||||
/* XXX: Can't hold lock across dup_bss due to recursive locking. */
|
||||
nt = &vap->iv_ic->ic_sta;
|
||||
IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
|
||||
+ hash = IEEE80211_NODE_HASH(mac);
|
||||
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
|
||||
+ if (ni->ni_vap != vap)
|
||||
+ continue;
|
||||
+
|
||||
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, mac)) {
|
||||
#ifdef IEEE80211_DEBUG_REFCNT
|
||||
- ni = ieee80211_find_node_locked_debug(nt, mac, func, line);
|
||||
+ ieee80211_ref_node_debug(ni, func, line);
|
||||
#else
|
||||
- ni = ieee80211_find_node_locked(nt, mac);
|
||||
+ ieee80211_ref_node(ni);
|
||||
#endif
|
||||
+ goto found;
|
||||
+ }
|
||||
+ }
|
||||
+ ni = NULL;
|
||||
+found:
|
||||
IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
|
||||
|
||||
if (ni == NULL) {
|
||||
@@ -1961,13 +2009,29 @@ remove_worse_nodes(void *arg, struct iee
|
||||
}
|
||||
}
|
||||
|
||||
+static void
|
||||
+remove_duplicate_nodes(void *arg, struct ieee80211_node *ni)
|
||||
+{
|
||||
+ struct ieee80211_node *rni = arg;
|
||||
+
|
||||
+ if (ni == rni)
|
||||
+ return;
|
||||
+
|
||||
+ if (ni->ni_vap == rni->ni_vap)
|
||||
+ return;
|
||||
+
|
||||
+ ieee80211_node_leave(ni);
|
||||
+}
|
||||
+
|
||||
void
|
||||
ieee80211_node_join(struct ieee80211_node *ni, int resp)
|
||||
{
|
||||
struct ieee80211com *ic = ni->ni_ic;
|
||||
struct ieee80211vap *vap = ni->ni_vap;
|
||||
+ struct ieee80211_node *tni;
|
||||
int newassoc;
|
||||
|
||||
+ ieee80211_iterate_nodes(&ic->ic_sta, remove_duplicate_nodes, ni);
|
||||
if (ni->ni_associd == 0) {
|
||||
u_int16_t aid;
|
||||
|
||||
--- a/net80211/ieee80211_input.c
|
||||
+++ b/net80211/ieee80211_input.c
|
||||
@@ -227,15 +227,22 @@ ieee80211_input(struct ieee80211vap * va
|
||||
if (!dev)
|
||||
goto out;
|
||||
|
||||
+ if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
|
||||
+ goto out;
|
||||
+
|
||||
+ if (!vap->iv_bss)
|
||||
+ goto out;
|
||||
+
|
||||
/* initialize ni as in the previous API */
|
||||
if (ni_or_null == NULL) {
|
||||
/* This function does not 'own' vap->iv_bss, so we cannot
|
||||
* guarantee its existence during the following call, hence
|
||||
* briefly grab our own reference. */
|
||||
ni = ieee80211_ref_node(vap->iv_bss);
|
||||
+ KASSERT(ni != NULL, ("null node"));
|
||||
+ } else {
|
||||
+ ni->ni_inact = ni->ni_inact_reload;
|
||||
}
|
||||
- KASSERT(ni != NULL, ("null node"));
|
||||
- ni->ni_inact = ni->ni_inact_reload;
|
||||
|
||||
KASSERT(skb->len >= sizeof(struct ieee80211_frame_min),
|
||||
("frame length too short: %u", skb->len));
|
||||
@@ -933,16 +940,23 @@ int
|
||||
ieee80211_input_all(struct ieee80211com *ic,
|
||||
struct sk_buff *skb, int rssi, u_int64_t rtsf)
|
||||
{
|
||||
+ struct ieee80211_frame_min *wh = (struct ieee80211_frame_min *) skb->data;
|
||||
struct ieee80211vap *vap;
|
||||
int type = -1;
|
||||
|
||||
/* XXX locking */
|
||||
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
|
||||
+ struct ieee80211_node *ni = NULL;
|
||||
struct sk_buff *skb1;
|
||||
|
||||
if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP) &&
|
||||
+ !IEEE80211_IS_MULTICAST(wh->i_addr1))
|
||||
+ continue;
|
||||
+
|
||||
+ ni = ieee80211_find_rxnode(ic, vap, wh);
|
||||
if (TAILQ_NEXT(vap, iv_next) != NULL) {
|
||||
skb1 = skb_copy(skb, GFP_ATOMIC);
|
||||
if (skb1 == NULL) {
|
||||
@@ -954,8 +968,10 @@ ieee80211_input_all(struct ieee80211com
|
||||
skb1 = skb;
|
||||
skb = NULL;
|
||||
}
|
||||
- type = ieee80211_input(vap, NULL, skb1, rssi, rtsf);
|
||||
+ type = ieee80211_input(vap, ni, skb1, rssi, rtsf);
|
||||
}
|
||||
+
|
||||
+out:
|
||||
if (skb != NULL) /* no vaps, reclaim skb */
|
||||
ieee80211_dev_kfree_skb(&skb);
|
||||
return type;
|
||||
@@ -1146,11 +1162,9 @@ ieee80211_deliver_data(struct ieee80211_
|
||||
* sending it will not work; just let it be
|
||||
* delivered normally.
|
||||
*/
|
||||
- struct ieee80211_node *ni1 = ieee80211_find_node(
|
||||
- &vap->iv_ic->ic_sta, eh->ether_dhost);
|
||||
+ struct ieee80211_node *ni1 = ieee80211_find_txnode(vap, eh->ether_dhost);
|
||||
if (ni1 != NULL) {
|
||||
- if (ni1->ni_vap == vap &&
|
||||
- ieee80211_node_is_authorized(ni1) &&
|
||||
+ if (ieee80211_node_is_authorized(ni1) &&
|
||||
!ni1->ni_subif &&
|
||||
ni1 != vap->iv_bss) {
|
||||
|
||||
--- a/ath/if_ath.c
|
||||
+++ b/ath/if_ath.c
|
||||
@@ -6577,9 +6577,8 @@ ath_recv_mgmt(struct ieee80211vap * vap,
|
||||
|
||||
sc->sc_recv_mgmt(vap, ni_or_null, skb, subtype, rssi, rtsf);
|
||||
|
||||
-
|
||||
/* Lookup the new node if any (this grabs a reference to it) */
|
||||
- ni = ieee80211_find_rxnode(vap->iv_ic,
|
||||
+ ni = ieee80211_find_rxnode(vap->iv_ic, vap,
|
||||
(const struct ieee80211_frame_min *)skb->data);
|
||||
if (ni == NULL) {
|
||||
DPRINTF(sc, ATH_DEBUG_BEACON, "Dropping; node unknown.\n");
|
||||
@@ -6734,7 +6733,9 @@ ath_rx_poll(struct net_device *dev, int
|
||||
struct ath_desc *ds;
|
||||
struct ath_rx_status *rs;
|
||||
struct sk_buff *skb = NULL;
|
||||
+ struct ieee80211vap *vap;
|
||||
struct ieee80211_node *ni;
|
||||
+ const struct ieee80211_frame_min *wh;
|
||||
unsigned int len;
|
||||
int type;
|
||||
u_int phyerr;
|
||||
@@ -6889,12 +6890,15 @@ rx_accept:
|
||||
skb_trim(skb, skb->len - IEEE80211_CRC_LEN);
|
||||
|
||||
if (mic_fail) {
|
||||
+ wh = (const struct ieee80211_frame_min *) skb->data;
|
||||
+
|
||||
/* Ignore control frames which are reported with mic error */
|
||||
- if ((((struct ieee80211_frame *)skb->data)->i_fc[0] &
|
||||
+ if ((wh->i_fc[0] &
|
||||
IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
|
||||
goto drop_micfail;
|
||||
|
||||
- ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) skb->data);
|
||||
+ vap = ieee80211_find_rxvap(ic, wh->i_addr1);
|
||||
+ ni = ieee80211_find_rxnode(ic, vap, wh);
|
||||
|
||||
if (ni && ni->ni_table) {
|
||||
ieee80211_check_mic(ni, skb);
|
||||
@@ -6956,11 +6960,24 @@ drop_micfail:
|
||||
* for its use. If the sender is unknown spam the
|
||||
* frame; it'll be dropped where it's not wanted.
|
||||
*/
|
||||
- if (rs->rs_keyix != HAL_RXKEYIX_INVALID &&
|
||||
+ wh = (const struct ieee80211_frame_min *) skb->data;
|
||||
+ if ((rs->rs_keyix != HAL_RXKEYIX_INVALID) &&
|
||||
(ni = sc->sc_keyixmap[rs->rs_keyix]) != NULL) {
|
||||
/* Fast path: node is present in the key map;
|
||||
* grab a reference for processing the frame. */
|
||||
- ni = ieee80211_ref_node(ni);
|
||||
+ ieee80211_ref_node(ni);
|
||||
+ if ((ATH_GET_VAP_ID(wh->i_addr1) !=
|
||||
+ ATH_GET_VAP_ID(ni->ni_vap->iv_myaddr)) ||
|
||||
+ ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
|
||||
+ IEEE80211_FC1_DIR_DSTODS)) {
|
||||
+ /* key cache node lookup is fast, but it can
|
||||
+ * lead to problems in multi-bss (foreign vap
|
||||
+ * node reference) or wds (wdsap node ref instead
|
||||
+ * of base ap node ref).
|
||||
+ * use slowpath lookup in both cases
|
||||
+ */
|
||||
+ goto lookup_slowpath;
|
||||
+ }
|
||||
ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
|
||||
type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
|
||||
ieee80211_unref_node(&ni);
|
||||
@@ -6969,24 +6986,35 @@ drop_micfail:
|
||||
* No key index or no entry, do a lookup and
|
||||
* add the node to the mapping table if possible.
|
||||
*/
|
||||
- ni = ieee80211_find_rxnode(ic,
|
||||
- (const struct ieee80211_frame_min *)skb->data);
|
||||
+
|
||||
+lookup_slowpath:
|
||||
+ vap = ieee80211_find_rxvap(ic, wh->i_addr1);
|
||||
+ if (vap)
|
||||
+ ni = ieee80211_find_rxnode(ic, vap, wh);
|
||||
+ else
|
||||
+ ni = NULL;
|
||||
+
|
||||
if (ni != NULL) {
|
||||
ieee80211_keyix_t keyix;
|
||||
|
||||
ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
|
||||
- type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
|
||||
+ type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
|
||||
/*
|
||||
* If the station has a key cache slot assigned
|
||||
* update the key->node mapping table.
|
||||
*/
|
||||
keyix = ni->ni_ucastkey.wk_keyix;
|
||||
if (keyix != IEEE80211_KEYIX_NONE &&
|
||||
- sc->sc_keyixmap[keyix] == NULL)
|
||||
+ sc->sc_keyixmap[keyix] == NULL) {
|
||||
sc->sc_keyixmap[keyix] = ieee80211_ref_node(ni);
|
||||
+ }
|
||||
ieee80211_unref_node(&ni);
|
||||
- } else
|
||||
- type = ieee80211_input_all(ic, skb, rs->rs_rssi, bf->bf_tsf);
|
||||
+ } else {
|
||||
+ if (vap)
|
||||
+ type = ieee80211_input(vap, NULL, skb, rs->rs_rssi, bf->bf_tsf);
|
||||
+ else
|
||||
+ type = ieee80211_input_all(ic, skb, rs->rs_rssi, bf->bf_tsf);
|
||||
+ }
|
||||
}
|
||||
|
||||
if (sc->sc_diversity) {
|
||||
--- a/net80211/ieee80211_node.h
|
||||
+++ b/net80211/ieee80211_node.h
|
||||
@@ -286,15 +286,18 @@ struct ieee80211_node *ieee80211_find_no
|
||||
const u_int8_t *);
|
||||
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
|
||||
|
||||
+struct ieee80211vap *
|
||||
+ieee80211_find_rxvap(struct ieee80211com *ic, const u_int8_t *mac);
|
||||
+
|
||||
/* Returns a ieee80211_node* with refcount incremented, if found */
|
||||
#ifdef IEEE80211_DEBUG_REFCNT
|
||||
-#define ieee80211_find_rxnode(_nt, _wh) \
|
||||
- ieee80211_find_rxnode_debug(_nt, _wh, __func__, __LINE__)
|
||||
+#define ieee80211_find_rxnode(_nt, _vap, _wh) \
|
||||
+ ieee80211_find_rxnode_debug(_nt, _vap, _wh, __func__, __LINE__)
|
||||
struct ieee80211_node *ieee80211_find_rxnode_debug(struct ieee80211com *,
|
||||
- const struct ieee80211_frame_min *, const char *, int);
|
||||
+ struct ieee80211vap *, const struct ieee80211_frame_min *, const char *, int);
|
||||
#else
|
||||
struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *,
|
||||
- const struct ieee80211_frame_min *);
|
||||
+ struct ieee80211vap *, const struct ieee80211_frame_min *);
|
||||
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
|
||||
|
||||
/* Returns a ieee80211_node* with refcount incremented, if found */
|
|
@ -1,6 +1,6 @@
|
|||
--- a/net80211/ieee80211_input.c
|
||||
+++ b/net80211/ieee80211_input.c
|
||||
@@ -3604,6 +3604,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
|
||||
@@ -3618,6 +3618,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
|
||||
vap->iv_stats.is_rx_mgtdiscard++;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#define IEEE80211_QOS_TXOP 0x00ff
|
||||
--- a/net80211/ieee80211_input.c
|
||||
+++ b/net80211/ieee80211_input.c
|
||||
@@ -429,7 +429,7 @@ ieee80211_input(struct ieee80211vap * va
|
||||
@@ -436,7 +436,7 @@ ieee80211_input(struct ieee80211vap * va
|
||||
tid = 0;
|
||||
rxseq = le16toh(*(__le16 *)wh->i_seq);
|
||||
if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
/*
|
||||
* Check if the MAC has multi-rate retry support.
|
||||
* We do this by trying to setup a fake extended
|
||||
@@ -7524,7 +7532,7 @@ ath_txq_setup(struct ath_softc *sc, int
|
||||
@@ -7552,7 +7560,7 @@ ath_txq_setup(struct ath_softc *sc, int
|
||||
if (qtype == HAL_TX_QUEUE_UAPSD)
|
||||
qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE;
|
||||
else
|
||||
|
|
Loading…
Reference in a new issue