]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hostapd: Extend support for HT 20/40 coexistence feature
authorPeng Xu <pxu@qca.qualcomm.com>
Mon, 14 Apr 2014 17:40:56 +0000 (20:40 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 29 Apr 2014 09:52:09 +0000 (12:52 +0300)
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>
src/ap/ctrl_iface_ap.c
src/ap/drv_callbacks.c
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/hw_features.c
src/ap/ieee802_11.h
src/ap/ieee802_11_ht.c
src/ap/sta_info.c
src/ap/sta_info.h
src/common/ieee802_11_defs.h

index 976093342154f786fc28800108fdf9934f5b09de..ccbbab5b0194409b1f089f4ff983b3a4edfc8966 100644 (file)
@@ -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)
index a8c24ebdf800f7c79ec48075f0591148eadc6b12..b3cfade9fd039b70aeab71d8f222fefa1dae58f5 100644 (file)
@@ -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)
index 614a5bf4001a63edeed35b9b52bc4123bcc3f225..33988598aadad28bb5a1a7ab5f37549573e41d84 100644 (file)
@@ -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;
 
index 090544d5af93730d4768071fd8ff1d143064ba2a..dba9bb29c6b3c76dceb8120d1cf454858d90cd5b 100644 (file)
@@ -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 */
index d47a366b8b75637908bd833c39f5ae6b7e1d1028..43cb243b9335e4b6983ade126345d714bf28f747 100644 (file)
@@ -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);
 }
 
index 809b4ca6c088ab8e2b9bf62cb7bac05f2107eaea..5b9437151bd819eb9cb8ab6e1c12fc8b20a22047 100644 (file)
@@ -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,
index 1d64748fcbd35899f06b857bc69eea0f10b84294..38caaed27da0c782c98a8e03e337aabb5d37158d 100644 (file)
 #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);
+}
index f5417de0c62ac6350e9239fd739ab33ea42be7a9..60f07682ec69fe97609a4602c4ed31fc3c1e5d22 100644 (file)
@@ -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;
index 2dbdeb18181b4db848e60fa29ec7ca2a2f088e12..03db98f66760e40d2cc22f400fdf6b0c80329aa1 100644 (file)
@@ -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;
index cb70130d3fadb7650f294dd1b8cf4b92481da6f1..8fe2e4a67210a9103f8becb355544a7445bdf9a1 100644 (file)
@@ -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))