openwrt_deco_e4r/package/hostapd/patches/638-add-support-for-HT-20-40-coexistence.patch

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))