hostapd: Extend support for HT 20/40 coexistence feature
Extend the minimal HT 20/40 co-ex support to include dynamic changes during the lifetime of the BSS. If any STA connects to a 2.4 GHz AP with 40 MHz intolerant bit set then the AP will switch to 20 MHz operating mode. If for a period of time specified by OBSS delay factor and OBSS scan interval AP does not have any information about 40 MHz intolerant STAs, the BSS is switched from HT20 to HT40 mode. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
196c9c7cd2
commit
9c47f6a2a6
10 changed files with 140 additions and 1 deletions
|
@ -405,6 +405,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
|
|||
"num_sta_ht_no_gf=%d\n"
|
||||
"num_sta_no_ht=%d\n"
|
||||
"num_sta_ht_20_mhz=%d\n"
|
||||
"num_sta_ht40_intolerant=%d\n"
|
||||
"olbc_ht=%d\n"
|
||||
"ht_op_mode=0x%x\n",
|
||||
hostapd_state_text(iface->state),
|
||||
|
@ -417,6 +418,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
|
|||
iface->num_sta_ht_no_gf,
|
||||
iface->num_sta_no_ht,
|
||||
iface->num_sta_ht_20mhz,
|
||||
iface->num_sta_ht40_intolerant,
|
||||
iface->olbc_ht,
|
||||
iface->ht_op_mode);
|
||||
if (ret < 0 || (size_t) ret >= buflen - len)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "radius/radius.h"
|
||||
#include "drivers/driver.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
|
@ -30,6 +31,7 @@
|
|||
#include "ap_config.h"
|
||||
#include "hw_features.h"
|
||||
#include "dfs.h"
|
||||
#include "beacon.h"
|
||||
|
||||
|
||||
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||
|
@ -121,6 +123,24 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
|
|||
}
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
#ifdef NEED_AP_MLME
|
||||
if (elems.ht_capabilities &&
|
||||
elems.ht_capabilities_len >=
|
||||
sizeof(struct ieee80211_ht_capabilities) &&
|
||||
(hapd->iface->conf->ht_capab &
|
||||
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
|
||||
struct ieee80211_ht_capabilities *ht_cap =
|
||||
(struct ieee80211_ht_capabilities *)
|
||||
elems.ht_capabilities;
|
||||
|
||||
if (le_to_host16(ht_cap->ht_capabilities_info) &
|
||||
HT_CAP_INFO_40MHZ_INTOLERANT)
|
||||
ht40_intolerant_add(hapd->iface, sta);
|
||||
}
|
||||
#endif /* NEED_AP_MLME */
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
|
||||
#ifdef CONFIG_INTERWORKING
|
||||
if (elems.ext_capab && elems.ext_capab_len > 4) {
|
||||
if (elems.ext_capab[4] & 0x01)
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "p2p_hostapd.h"
|
||||
#include "gas_serv.h"
|
||||
#include "dfs.h"
|
||||
#include "ieee802_11.h"
|
||||
|
||||
|
||||
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
|
||||
|
@ -1352,6 +1353,11 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
|
|||
if (iface == NULL)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
#ifdef NEED_AP_MLME
|
||||
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
|
||||
#endif /* NEED_AP_MLME */
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
|
||||
iface->wait_channel_update = 0;
|
||||
|
||||
|
|
|
@ -327,6 +327,9 @@ struct hostapd_iface {
|
|||
/* Number of HT associated stations 20 MHz */
|
||||
int num_sta_ht_20mhz;
|
||||
|
||||
/* Number of HT40 intolerant stations */
|
||||
int num_sta_ht40_intolerant;
|
||||
|
||||
/* Overlapping BSS information */
|
||||
int olbc_ht;
|
||||
|
||||
|
@ -351,6 +354,10 @@ struct hostapd_iface {
|
|||
unsigned int dfs_cac_ms;
|
||||
struct os_reltime dfs_cac_start;
|
||||
|
||||
/* Latched with the actual secondary channel information and will be
|
||||
* used while juggling between HT20 and HT40 modes. */
|
||||
int secondary_ch;
|
||||
|
||||
#ifdef CONFIG_ACS
|
||||
unsigned int acs_num_completed_scans;
|
||||
#endif /* CONFIG_ACS */
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "ap_config.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "acs.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "beacon.h"
|
||||
#include "hw_features.h"
|
||||
|
||||
|
||||
|
@ -414,6 +416,7 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
|
|||
int pri = bss->freq;
|
||||
int sec = pri;
|
||||
int sec_chan, pri_chan;
|
||||
struct ieee802_11_elems elems;
|
||||
|
||||
ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
|
||||
|
||||
|
@ -445,7 +448,23 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
|
|||
}
|
||||
}
|
||||
|
||||
/* TODO: 40 MHz intolerant */
|
||||
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
|
||||
0);
|
||||
if (elems.ht_capabilities &&
|
||||
elems.ht_capabilities_len >=
|
||||
sizeof(struct ieee80211_ht_capabilities)) {
|
||||
struct ieee80211_ht_capabilities *ht_cap =
|
||||
(struct ieee80211_ht_capabilities *)
|
||||
elems.ht_capabilities;
|
||||
|
||||
if (le_to_host16(ht_cap->ht_capabilities_info) &
|
||||
HT_CAP_INFO_40MHZ_INTOLERANT) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"40 MHz Intolerant is set on channel %d in BSS "
|
||||
MACSTR, pri, MAC2STR(bss->bssid));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -475,6 +494,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
|
|||
oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
|
||||
wpa_scan_results_free(scan_res);
|
||||
|
||||
iface->secondary_ch = iface->conf->secondary_channel;
|
||||
if (!oper40) {
|
||||
wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
|
||||
"channel pri=%d sec=%d based on overlapping BSSes",
|
||||
|
@ -482,9 +502,21 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
|
|||
iface->conf->channel +
|
||||
iface->conf->secondary_channel * 4);
|
||||
iface->conf->secondary_channel = 0;
|
||||
if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
|
||||
/*
|
||||
* TODO: Could consider scheduling another scan to check
|
||||
* if channel width can be changed if no coex reports
|
||||
* are received from associating stations.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
res = ieee80211n_allowed_ht40_channel_pair(iface);
|
||||
if (!res) {
|
||||
iface->conf->secondary_channel = 0;
|
||||
wpa_printf(MSG_INFO, "Fallback to 20 MHz");
|
||||
}
|
||||
|
||||
hostapd_setup_interface_complete(iface, !res);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
|
|||
#endif /* NEED_AP_MLME */
|
||||
u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
int probe);
|
||||
void ap_ht2040_timeout(void *eloop_data, void *user_data);
|
||||
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
|
||||
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
|
||||
|
@ -59,6 +60,8 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd,
|
|||
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *ht_capab, size_t ht_capab_len);
|
||||
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
|
||||
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
|
||||
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
const u8 *vht_capab, size_t vht_capab_len);
|
||||
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
|
|
|
@ -10,12 +10,15 @@
|
|||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "sta_info.h"
|
||||
#include "beacon.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "hw_features.h"
|
||||
#include "ap_drv_ops.h"
|
||||
|
||||
|
||||
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
|
||||
|
@ -200,6 +203,52 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
|||
}
|
||||
|
||||
|
||||
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
|
||||
{
|
||||
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
|
||||
return;
|
||||
|
||||
wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
|
||||
" in Association Request", MAC2STR(sta->addr));
|
||||
|
||||
if (sta->ht40_intolerant_set)
|
||||
return;
|
||||
|
||||
sta->ht40_intolerant_set = 1;
|
||||
iface->num_sta_ht40_intolerant++;
|
||||
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
|
||||
|
||||
if (iface->conf->secondary_channel &&
|
||||
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||
iface->conf->secondary_channel = 0;
|
||||
ieee802_11_set_beacons(iface);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
|
||||
{
|
||||
if (!sta->ht40_intolerant_set)
|
||||
return;
|
||||
|
||||
sta->ht40_intolerant_set = 0;
|
||||
iface->num_sta_ht40_intolerant--;
|
||||
|
||||
if (iface->num_sta_ht40_intolerant == 0 &&
|
||||
(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
|
||||
(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
|
||||
unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
|
||||
iface->conf->obss_interval;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"HT: Start 20->40 MHz transition timer (%d seconds)",
|
||||
delay_time);
|
||||
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
|
||||
eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
|
||||
iface, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
u16 ht_capab;
|
||||
|
@ -227,6 +276,9 @@ static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
|
|||
__func__, MAC2STR(sta->addr),
|
||||
hapd->iface->num_sta_ht_20mhz);
|
||||
}
|
||||
|
||||
if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
|
||||
ht40_intolerant_add(hapd->iface, sta);
|
||||
}
|
||||
|
||||
|
||||
|
@ -288,3 +340,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd,
|
|||
|
||||
neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
|
||||
}
|
||||
|
||||
|
||||
void ap_ht2040_timeout(void *eloop_data, void *user_data)
|
||||
{
|
||||
struct hostapd_iface *iface = eloop_data;
|
||||
|
||||
wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
|
||||
|
||||
iface->conf->secondary_channel = iface->secondary_ch;
|
||||
ieee802_11_set_beacons(iface);
|
||||
}
|
||||
|
|
|
@ -206,6 +206,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
|
|||
hapd->iface->num_sta_ht_20mhz--;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
ht40_intolerant_remove(hapd->iface, sta);
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
if (sta->no_p2p_set) {
|
||||
sta->no_p2p_set = 0;
|
||||
|
|
|
@ -54,6 +54,7 @@ struct sta_info {
|
|||
unsigned int no_short_preamble_set:1;
|
||||
unsigned int no_ht_gf_set:1;
|
||||
unsigned int no_ht_set:1;
|
||||
unsigned int ht40_intolerant_set:1;
|
||||
unsigned int ht_20mhz_set:1;
|
||||
unsigned int no_p2p_set:1;
|
||||
unsigned int qos_map_enabled:1;
|
||||
|
|
|
@ -646,6 +646,7 @@ struct ieee80211_vht_operation {
|
|||
#define ERP_INFO_USE_PROTECTION BIT(1)
|
||||
#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
|
||||
|
||||
#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
|
||||
|
||||
/* HT Capabilities Info field within HT Capabilities element */
|
||||
#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0))
|
||||
|
|
Loading…
Reference in a new issue