314 lines
9.7 KiB
Diff
314 lines
9.7 KiB
Diff
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
|
|
index b4f1204..2253925 100644
|
|
--- a/src/ap/drv_callbacks.c
|
|
+++ b/src/ap/drv_callbacks.c
|
|
@@ -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"
|
|
@@ -29,6 +30,7 @@
|
|
#include "ap_drv_ops.h"
|
|
#include "ap_config.h"
|
|
#include "hw_features.h"
|
|
+#include "beacon.h"
|
|
|
|
|
|
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
|
|
@@ -111,6 +113,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_HS20
|
|
wpabuf_free(sta->hs20_ie);
|
|
if (elems.hs20 && elems.hs20_len > 4) {
|
|
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
|
|
index 3a2b217..cc310e0 100644
|
|
--- a/src/ap/hostapd.c
|
|
+++ b/src/ap/hostapd.c
|
|
@@ -1118,6 +1118,12 @@ 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 */
|
|
+
|
|
hostapd_cleanup_iface_pre(iface);
|
|
for (j = 0; j < iface->num_bss; j++) {
|
|
struct hostapd_data *hapd = iface->bss[j];
|
|
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
|
|
index f8c4616..d9ab505 100644
|
|
--- a/src/ap/hostapd.h
|
|
+++ b/src/ap/hostapd.h
|
|
@@ -271,6 +271,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;
|
|
|
|
@@ -279,6 +282,10 @@ struct hostapd_iface {
|
|
|
|
u16 ht_op_mode;
|
|
void (*scan_cb)(struct hostapd_iface *iface);
|
|
+
|
|
+ /* Latched with the actual secondary channel information and will be
|
|
+ * used while juggling between HT20 and HT40 modes. */
|
|
+ int secondary_ch;
|
|
};
|
|
|
|
/**
|
|
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
|
|
index 918084d..5a87821 100644
|
|
--- a/src/ap/hw_features.c
|
|
+++ b/src/ap/hw_features.c
|
|
@@ -25,6 +25,8 @@
|
|
#include "ap_config.h"
|
|
#include "ap_drv_ops.h"
|
|
#include "hw_features.h"
|
|
+#include "ieee802_11.h"
|
|
+#include "beacon.h"
|
|
|
|
|
|
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
|
|
@@ -375,6 +377,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);
|
|
|
|
@@ -406,7 +409,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;
|
|
@@ -436,6 +455,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",
|
|
@@ -443,9 +463,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);
|
|
}
|
|
|
|
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
|
|
index 868bfef..98ddb59 100644
|
|
--- a/src/ap/ieee802_11.h
|
|
+++ b/src/ap/ieee802_11.h
|
|
@@ -40,6 +40,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_supp_rates(struct hostapd_data *hapd, u8 *eid);
|
|
u8 * hostapd_eid_ext_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);
|
|
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
|
|
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
|
|
index 4ad15ef..22b58e5 100644
|
|
--- a/src/ap/ieee802_11_ht.c
|
|
+++ b/src/ap/ieee802_11_ht.c
|
|
@@ -25,6 +25,7 @@
|
|
#include "beacon.h"
|
|
#include "ieee802_11.h"
|
|
#include "utils/eloop.h"
|
|
+#include "hw_features.h"
|
|
|
|
|
|
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
|
|
@@ -216,6 +217,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;
|
|
@@ -243,6 +290,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);
|
|
}
|
|
|
|
|
|
@@ -385,3 +435,14 @@ void hostapd_trigger_20mhz(struct hostapd_iface *iface)
|
|
|
|
os_get_time(&iface->last_20mhz_trigger);
|
|
}
|
|
+
|
|
+
|
|
+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);
|
|
+}
|
|
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
|
|
index d38330f..136d8ee 100644
|
|
--- a/src/ap/sta_info.c
|
|
+++ b/src/ap/sta_info.c
|
|
@@ -180,6 +180,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;
|
|
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
|
|
index f8f5a83..5347ced 100644
|
|
--- a/src/ap/sta_info.h
|
|
+++ b/src/ap/sta_info.h
|
|
@@ -55,6 +55,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;
|
|
|
|
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
|
|
index d2e458d..b37b704 100644
|
|
--- a/src/common/ieee802_11_defs.h
|
|
+++ b/src/common/ieee802_11_defs.h
|
|
@@ -650,6 +650,7 @@ struct ieee80211_vht_operation {
|
|
#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10))
|
|
#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11))
|
|
|
|
+#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
|
|
|
|
#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0))
|
|
#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
|