Cumulative patch from commit c0333c8dd59b9e254d3fc725c984b1c0cd3d496a

c0333c8 Check rx_mgmt::frame more consistently against NULL
d6c6b1f Make sta NULL-check easier for static analyzers
0bceb8d Make dl_list_first() and dl_list_last() uses easier for static analyzers
5f693cb WPS HTTP: Remove unused assignment
ee4fefc Remove duplicated variable zeroing
4a9d0eb Make PMKID check easier for static analyzers
06df2aa Remove floating constant suffix 'd' from test coee
9670f87 ACS: Clean up ifdef CONFIG_ACS to avoid unreachable code
ece88f7 Make last_scan_res update easier for static analyzers
d06e9ac P2P: Verify operating channel validity for NFC connection handover
13a524a nl80211: Remove unnecessary wpa_driver_nl80211_set_freq() wrapper
e87ef75 nl80211: Add support for changing AP mode channel bandwidth
3057518 Sync with mac80211-next.git nl80211.h
5f0bca7 Retry initial 20/40 MHz co-ex scan if the driver is busy
587d60d Add AP mode support for HT 20/40 co-ex Action frame
9c47f6a hostapd: Extend support for HT 20/40 coexistence feature
196c9c7 Make channel parameters available in set_ap() driver operation
b7a6702 Indicate disconnection event on interface disabled
b89962b Fix wpa_config_read() with existing config block
27b4187 WPS: Print setsockopt() failure in debug log
52cb207 trace: Replace demangle.h with internal defines
0e80ea2 nl80211: Fix some coding style issues
a26582c Make qca-vendor.h independent of other header files
4a64d5a nl80211: Allocate QCA vendor subcmd for extended statistics

Change-Id: Ibabee77b2b87f04f99d6b1ca0f1ac741cb9222c0
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 0a143d3..9680817 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -174,11 +174,11 @@
 	conf->ap_table_expiration_time = 60;
 
 #ifdef CONFIG_TESTING_OPTIONS
-	conf->ignore_probe_probability = 0.0d;
-	conf->ignore_auth_probability = 0.0d;
-	conf->ignore_assoc_probability = 0.0d;
-	conf->ignore_reassoc_probability = 0.0d;
-	conf->corrupt_gtk_rekey_mic_probability = 0.0d;
+	conf->ignore_probe_probability = 0.0;
+	conf->ignore_auth_probability = 0.0;
+	conf->ignore_assoc_probability = 0.0;
+	conf->ignore_reassoc_probability = 0.0;
+	conf->corrupt_gtk_rekey_mic_probability = 0.0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_ACS
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 56df318..27525dc 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -645,7 +645,7 @@
 	 * with AP configuration */
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (hapd->iconf->ignore_probe_probability > 0.0d &&
+	if (hapd->iconf->ignore_probe_probability > 0.0 &&
 	    drand48() < hapd->iconf->ignore_probe_probability) {
 		wpa_printf(MSG_INFO,
 			   "TESTING: ignoring probe request from " MACSTR,
@@ -951,6 +951,9 @@
 int ieee802_11_set_beacon(struct hostapd_data *hapd)
 {
 	struct wpa_driver_ap_params params;
+	struct hostapd_freq_params freq;
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_config *iconf = iface->conf;
 	struct wpabuf *beacon, *proberesp, *assocresp;
 	int res, ret = -1;
 
@@ -972,6 +975,17 @@
 	params.proberesp_ies = proberesp;
 	params.assocresp_ies = assocresp;
 
+	if (iface->current_mode &&
+	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
+				    iconf->channel, iconf->ieee80211n,
+				    iconf->ieee80211ac,
+				    iconf->secondary_channel,
+				    iconf->vht_oper_chwidth,
+				    iconf->vht_oper_centr_freq_seg0_idx,
+				    iconf->vht_oper_centr_freq_seg1_idx,
+				    iface->current_mode->vht_capab) == 0)
+		params.freq = &freq;
+
 	res = hostapd_drv_set_ap(hapd, &params);
 	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
 	if (res)
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 9760933..ccbbab5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -405,6 +405,7 @@
 			  "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 @@
 			  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)
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index a8c24eb..fb095ef 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"
@@ -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 @@
 	}
 #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)
@@ -981,6 +1001,8 @@
 		break;
 #endif /* NEED_AP_MLME */
 	case EVENT_RX_MGMT:
+		if (!data->rx_mgmt.frame)
+			break;
 #ifdef NEED_AP_MLME
 		if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0)
 			break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 614a5bf..391d774 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -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,12 @@
 	if (iface == NULL)
 		return;
 
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+	hostapd_stop_setup_timers(iface);
+	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;
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 090544d..bd85c54 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -327,6 +327,9 @@
 	/* 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,11 +354,16 @@
 	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 */
 
 	void (*scan_cb)(struct hostapd_iface *iface);
+	int num_ht40_scan_tries;
 };
 
 /* hostapd.c */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index d47a366..b361834 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -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 @@
 		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 @@
 			}
 		}
 
-		/* 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 @@
 		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 @@
 			   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);
 }
 
@@ -570,9 +602,55 @@
 }
 
 
+static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
+{
+#define HT2040_COEX_SCAN_RETRY 15
+	struct hostapd_iface *iface = eloop_data;
+	struct wpa_driver_scan_params params;
+	int ret;
+
+	os_memset(&params, 0, sizeof(params));
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		ieee80211n_scan_channels_2g4(iface, &params);
+	else
+		ieee80211n_scan_channels_5g(iface, &params);
+
+	ret = hostapd_driver_scan(iface->bss[0], &params);
+	iface->num_ht40_scan_tries++;
+	os_free(params.freqs);
+
+	if (ret == -EBUSY &&
+	    iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
+			   ret, strerror(-ret), iface->num_ht40_scan_tries);
+		eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+		return;
+	}
+
+	if (ret == 0) {
+		iface->scan_cb = ieee80211n_check_scan;
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "Failed to request a scan in device, bringing up in HT20 mode");
+	iface->conf->secondary_channel = 0;
+	iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+	hostapd_setup_interface_complete(iface, 0);
+}
+
+
+void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+	eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+}
+
+
 static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
 {
 	struct wpa_driver_scan_params params;
+	int ret;
 
 	if (!iface->conf->secondary_channel)
 		return 0; /* HT40 not used */
@@ -585,13 +663,26 @@
 		ieee80211n_scan_channels_2g4(iface, &params);
 	else
 		ieee80211n_scan_channels_5g(iface, &params);
-	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
-		wpa_printf(MSG_ERROR, "Failed to request a scan of "
-			   "neighboring BSSes");
-		os_free(params.freqs);
+
+	ret = hostapd_driver_scan(iface->bss[0], &params);
+	os_free(params.freqs);
+
+	if (ret == -EBUSY) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
+			   ret, strerror(-ret));
+		iface->num_ht40_scan_tries = 1;
+		eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+		eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+		return 1;
+	}
+
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s)",
+			   ret, strerror(-ret));
 		return -1;
 	}
-	os_free(params.freqs);
 
 	iface->scan_cb = ieee80211n_check_scan;
 	return 1;
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index 783ae5e..0f67ab8 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -23,6 +23,7 @@
 int hostapd_check_ht_capab(struct hostapd_iface *iface);
 int hostapd_prepare_rates(struct hostapd_iface *iface,
 			  struct hostapd_hw_modes *mode);
+void hostapd_stop_setup_timers(struct hostapd_iface *iface);
 #else /* NEED_AP_MLME */
 static inline void
 hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -61,6 +62,10 @@
 	return 0;
 }
 
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
 #endif /* NEED_AP_MLME */
 
 #endif /* HW_FEATURES_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 14fb567..ca8db8f 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -62,7 +62,6 @@
 	}
 
 	*pos++ = num;
-	count = 0;
 	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
 	     i++) {
 		count++;
@@ -105,7 +104,6 @@
 
 	*pos++ = WLAN_EID_EXT_SUPP_RATES;
 	*pos++ = num;
-	count = 0;
 	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
 	     i++) {
 		count++;
@@ -565,7 +563,7 @@
 	}
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (hapd->iconf->ignore_auth_probability > 0.0d &&
+	if (hapd->iconf->ignore_auth_probability > 0.0 &&
 	    drand48() < hapd->iconf->ignore_auth_probability) {
 		wpa_printf(MSG_INFO,
 			   "TESTING: ignoring auth frame from " MACSTR,
@@ -1291,7 +1289,7 @@
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (reassoc) {
-		if (hapd->iconf->ignore_reassoc_probability > 0.0d &&
+		if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
 		    drand48() < hapd->iconf->ignore_reassoc_probability) {
 			wpa_printf(MSG_INFO,
 				   "TESTING: ignoring reassoc request from "
@@ -1299,7 +1297,7 @@
 			return;
 		}
 	} else {
-		if (hapd->iconf->ignore_assoc_probability > 0.0d &&
+		if (hapd->iconf->ignore_assoc_probability > 0.0 &&
 		    drand48() < hapd->iconf->ignore_assoc_probability) {
 			wpa_printf(MSG_INFO,
 				   "TESTING: ignoring assoc request from "
@@ -1628,7 +1626,8 @@
 	switch (mgmt->u.action.category) {
 #ifdef CONFIG_IEEE80211R
 	case WLAN_ACTION_FT:
-		if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+		if (!sta ||
+		    wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
 				     len - IEEE80211_HDRLEN))
 			break;
 		return 1;
@@ -1647,6 +1646,15 @@
 #endif /* CONFIG_WNM */
 	case WLAN_ACTION_PUBLIC:
 	case WLAN_ACTION_PROTECTED_DUAL:
+#ifdef CONFIG_IEEE80211N
+		if (mgmt->u.action.u.public_action.action ==
+		    WLAN_PA_20_40_BSS_COEX) {
+			wpa_printf(MSG_DEBUG,
+				   "HT20/40 coex mgmt frame received from STA "
+				   MACSTR, MAC2STR(mgmt->sa));
+			hostapd_2040_coex_action(hapd, mgmt, len);
+		}
+#endif /* CONFIG_IEEE80211N */
 		if (hapd->public_action_cb) {
 			hapd->public_action_cb(hapd->public_action_cb_ctx,
 					       (u8 *) mgmt, len,
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 809b4ca..cf0d3f2 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -14,11 +14,14 @@
 struct sta_info;
 struct hostapd_frame_info;
 struct ieee80211_ht_capabilities;
+struct ieee80211_mgmt;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 		    struct hostapd_frame_info *fi);
 void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
 			u16 stype, int ok);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+			      const struct ieee80211_mgmt *mgmt, size_t len);
 #ifdef NEED_AP_MLME
 int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
 int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
@@ -39,6 +42,7 @@
 #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 +63,8 @@
 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,
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 1d64748..c0a7cd4 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -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)
@@ -172,6 +175,117 @@
 }
 
 
+static int is_40_allowed(struct hostapd_iface *iface, int channel)
+{
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	int pri = 2407 + 5 * channel;
+
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return 1;
+
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	if ((pri < affected_start || pri > affected_end))
+		return 1; /* not within affected channel range */
+
+	wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+	wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
+	return 0;
+}
+
+
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+			      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct ieee80211_2040_bss_coex_ie *bc_ie;
+	struct ieee80211_2040_intol_chan_report *ic_report;
+	int is_ht_allowed = 1;
+	int i;
+	const u8 *data = (const u8 *) &mgmt->u.action.u.public_action.action;
+	size_t hdr_len;
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+		       mgmt->u.action.u.public_action.action);
+
+	if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+		return;
+
+	hdr_len = data - (u8 *) mgmt;
+	if (hdr_len > len)
+		return;
+	data++;
+
+	bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0];
+	ic_report = (struct ieee80211_2040_intol_chan_report *)
+		(&data[0] + sizeof(*bc_ie));
+
+	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+		hostapd_logger(hapd, mgmt->sa,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "20 MHz BSS width request bit is set in BSS coexistence information field");
+		is_ht_allowed = 0;
+	}
+
+	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+		hostapd_logger(hapd, mgmt->sa,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "40 MHz intolerant bit is set in BSS coexistence information field");
+		is_ht_allowed = 0;
+	}
+
+	if (ic_report &&
+	    (ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) {
+		/* Go through the channel report to find any BSS there in the
+		 * affected channel range */
+		for (i = 0; i < ic_report->length - 1; i++) {
+			if (is_40_allowed(iface, ic_report->variable[i]))
+				continue;
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "20_40_INTOLERANT channel %d reported",
+				       ic_report->variable[i]);
+			is_ht_allowed = 0;
+			break;
+		}
+	}
+
+	if (!is_ht_allowed &&
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+		if (iface->conf->secondary_channel) {
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Switching to 20 MHz operation");
+			iface->conf->secondary_channel = 0;
+			ieee802_11_set_beacons(iface);
+		}
+		if (!iface->num_sta_ht40_intolerant) {
+			unsigned int delay_time;
+			delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+				iface->conf->obss_interval;
+			eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
+					     NULL);
+			eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+					       hapd->iface, NULL);
+		}
+	}
+}
+
+
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab, size_t ht_capab_len)
 {
@@ -200,6 +314,52 @@
 }
 
 
+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 +387,9 @@
 			   __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 +451,14 @@
 
 	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);
+}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index f5417de..60f0768 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -206,6 +206,10 @@
 		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 2dbdeb1..03db98f 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -54,6 +54,7 @@
 	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;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 77e7858..d212610 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1421,7 +1421,7 @@
 				  key->key_mic);
 #ifdef CONFIG_TESTING_OPTIONS
 		if (!pairwise &&
-		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d &&
+		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
 		    drand48() <
 		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index e957c6e..1e4defc 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -675,7 +675,7 @@
 			break;
 		}
 	}
-	if (sm->pmksa) {
+	if (sm->pmksa && pmkid) {
 		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 				 "PMKID found from PMKSA cache "
 				 "eap_type=%d vlan_id=%d",