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 9760933..ccbbab5 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 a8c24eb..b3cfade 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 614a5bf..3398859 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 090544d..dba9bb2 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 d47a366..43cb243 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 809b4ca..5b94371 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 1d64748..38caaed 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 f5417de..60f0768 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 2dbdeb1..03db98f 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 cb70130..8fe2e4a 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))