Cumulative patch from commit 538922a628d4f5403b9a96b171a59235bcb3d921

538922a dbus: Add boolean AllowRoam option to Scan() method options dictionary
c6f5dec Don't start second scan when changing scan interval
cd3b070 nl80211: Fix DFS radar event parsing
2b72df6 nl80211: Free BSS structure even if netdev does not exists
41cc50d nl80211: Update send_action_cookie on AP-offchannel-TX path
313424d GAS: Add support for multiple pending queries for the same destination
cbc5484 GAS: Do not start new scan operation during an ongoing GAS query
c377514 GAS: Delay GAS query Tx while scanning/connecting
24c694b GAS: Delay GAS query Tx while another query is in progress
7255983 WPS: Clear after_wps from number of new locations
73b54d6 P2P: Fix Operating Channel in Invitation Request for operating group
dc46fd6 P2P: Cancel offchannel TX wait on Invitation Response RX
0c92963 D-Bus: Clean up debug print for P2P invitation result
8d82c21 P2P: Fix PD retry channel on join-a-group case
d285888 P2P: Add GO BSS entry details to debug log on join-a-group
512629a P2P: Accept Invitation Response non-success without Channel List
e241b1b eap_proxy: Fix IMSI fetch for home vs. visited network determination
db13605 EAP-AKA/AKA' peer: Allow external USIM processing to be used
569ccf7 EAP-SIM peer: Allow external SIM processing to be used
84dc137 hlr_auc_gw: Add GSM-AUTH-REQ command
a5d44ac EAP peer: Add framework for external SIM/USIM processing
7e8bc7d eapol_test: Initialize BSS lists
bceb843 Send CTRL-RSP command response before processing EAPOL update
b607796 eapol_test: Fix external EAP request mechanism
94de082 eapol_test: Initialize wpa_s->global to fix ctrl_iface
f07bba3 Android: Add dfs.c into build
0cf0af2 WNM: Set Disassoc Imminent flag in ESS Disassoc Imminent frame
f47c145 Interworking: Add required_roaming_consortium parameter for credentials
a83e574 GAS: Update timeout from TX status handler
e88060e HTTP server: Allow TCP socket to be reused
9bc3386 Add test option for specifying hardcoded BSS Load element
9c7e43a Define BSS Load element id
56f5af4 Interworking: Add support for QoS Mapping functionality for the STA
850e1c2 atheros: Add support for QoS Mapping configuration
c551700 Interworking: Add support for QoS Mapping functionality for the AP
ac1bc54 Interworking: Add domain_suffix_match for credentials
463c8ff Interworking: Add support for multiple home FQDNs
01f809c Add AAA server domain name suffix matching constraint
be7963b OpenSSL: Fix code indentation in OCSP processing
899cc14 hostapd: Add support for DFS with 160 MHz channel width
6de0e0c Mark DFS functions static and rename them
58b73e3 hostapd: DFS with 40/80 MHz channel width support
846de15 DFS: Add more parameters to radar events
04e8003 nl80211: Use struct hostapd_freq_params with start_dfs_cac
72c753d hostapd: Split hostapd_set_freq to helper function
e76da50 hostapd: Add AP DFS support

Change-Id: Ie9ed4662ba6d81e6d8b14bccb29ffa192becf0f2
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c5531fa..4f6a739 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -442,6 +442,9 @@
 	u16 gas_comeback_delay;
 	int gas_frag_limit;
 
+	u8 qos_map_set[16 + 2 * 21];
+	unsigned int qos_map_set_len;
+
 #ifdef CONFIG_HS20
 	int hs20;
 	int disable_dgaf;
@@ -464,6 +467,11 @@
 
 	unsigned int sae_anti_clogging_threshold;
 	int *sae_groups;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 bss_load_test[5];
+	u8 bss_load_test_set;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 3072562..9023eab 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -463,44 +463,44 @@
 }
 
 
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-		     int channel, int ht_enabled, int vht_enabled,
-		     int sec_channel_offset, int vht_oper_chwidth,
-		     int center_segment0, int center_segment1)
+static int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode,
+				   int freq, int channel, int ht_enabled,
+				   int vht_enabled, int sec_channel_offset,
+				   int vht_oper_chwidth, int center_segment0,
+				   int center_segment1)
 {
-	struct hostapd_freq_params data;
 	int tmp;
 
-	os_memset(&data, 0, sizeof(data));
-	data.mode = mode;
-	data.freq = freq;
-	data.channel = channel;
-	data.ht_enabled = ht_enabled;
-	data.vht_enabled = vht_enabled;
-	data.sec_channel_offset = sec_channel_offset;
-	data.center_freq1 = freq + sec_channel_offset * 10;
-	data.center_freq2 = 0;
-	data.bandwidth = sec_channel_offset ? 40 : 20;
+	os_memset(data, 0, sizeof(*data));
+	data->mode = mode;
+	data->freq = freq;
+	data->channel = channel;
+	data->ht_enabled = ht_enabled;
+	data->vht_enabled = vht_enabled;
+	data->sec_channel_offset = sec_channel_offset;
+	data->center_freq1 = freq + sec_channel_offset * 10;
+	data->center_freq2 = 0;
+	data->bandwidth = sec_channel_offset ? 40 : 20;
 
 	/*
 	 * This validation code is probably misplaced, maybe it should be
 	 * in src/ap/hw_features.c and check the hardware support as well.
 	 */
-	if (data.vht_enabled) switch (vht_oper_chwidth) {
+	if (data->vht_enabled) switch (vht_oper_chwidth) {
 	case VHT_CHANWIDTH_USE_HT:
 		if (center_segment1)
 			return -1;
-		if (5000 + center_segment0 * 5 != data.center_freq1)
+		if (5000 + center_segment0 * 5 != data->center_freq1)
 			return -1;
 		break;
 	case VHT_CHANWIDTH_80P80MHZ:
 		if (center_segment1 == center_segment0 + 4 ||
 		    center_segment1 == center_segment0 - 4)
 			return -1;
-		data.center_freq2 = 5000 + center_segment1 * 5;
+		data->center_freq2 = 5000 + center_segment1 * 5;
 		/* fall through */
 	case VHT_CHANWIDTH_80MHZ:
-		data.bandwidth = 80;
+		data->bandwidth = 80;
 		if (vht_oper_chwidth == 1 && center_segment1)
 			return -1;
 		if (vht_oper_chwidth == 3 && !center_segment1)
@@ -510,13 +510,13 @@
 		/* primary 40 part must match the HT configuration */
 		tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
 		tmp /= 2;
-		if (data.center_freq1 != 5000 +
+		if (data->center_freq1 != 5000 +
 					 center_segment0 * 5 - 20 + 40 * tmp)
 			return -1;
-		data.center_freq1 = 5000 + center_segment0 * 5;
+		data->center_freq1 = 5000 + center_segment0 * 5;
 		break;
 	case VHT_CHANWIDTH_160MHZ:
-		data.bandwidth = 160;
+		data->bandwidth = 160;
 		if (center_segment1)
 			return -1;
 		if (!sec_channel_offset)
@@ -524,12 +524,30 @@
 		/* primary 40 part must match the HT configuration */
 		tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
 		tmp /= 2;
-		if (data.center_freq1 != 5000 +
+		if (data->center_freq1 != 5000 +
 					 center_segment0 * 5 - 60 + 40 * tmp)
 			return -1;
-		data.center_freq1 = 5000 + center_segment0 * 5;
+		data->center_freq1 = 5000 + center_segment0 * 5;
 		break;
 	}
+
+	return 0;
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+		     int channel, int ht_enabled, int vht_enabled,
+		     int sec_channel_offset, int vht_oper_chwidth,
+		     int center_segment0, int center_segment1)
+{
+	struct hostapd_freq_params data;
+
+	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+				    vht_enabled, sec_channel_offset,
+				    vht_oper_chwidth,
+				    center_segment0, center_segment1))
+		return -1;
+
 	if (hapd->driver == NULL)
 		return 0;
 	if (hapd->driver->set_freq == NULL)
@@ -699,3 +717,38 @@
 					 hapd->own_addr, hapd->own_addr, data,
 					 len, 0);
 }
+
+int hostapd_start_dfs_cac(struct hostapd_data *hapd, int mode, int freq,
+			  int channel, int ht_enabled, int vht_enabled,
+			  int sec_channel_offset, int vht_oper_chwidth,
+			  int center_segment0, int center_segment1)
+{
+	struct hostapd_freq_params data;
+
+	if (!hapd->driver || !hapd->driver->start_dfs_cac)
+		return 0;
+
+	if (!hapd->iface->conf->ieee80211h) {
+		wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+			   "is not enabled");
+		return -1;
+	}
+
+	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+				    vht_enabled, sec_channel_offset,
+				    vht_oper_chwidth, center_segment0,
+				    center_segment1))
+		return -1;
+
+	return hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+			    const u8 *qos_map_set, u8 qos_map_set_len)
+{
+	if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
+		return 0;
+	return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+					 qos_map_set_len);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 23277b6..8a2b4dc 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -101,6 +101,10 @@
 		      int reassoc, u16 status, const u8 *ie, size_t len);
 int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
 		      u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_data *hapd, int mode, int freq,
+			  int channel, int ht_enabled, int vht_enabled,
+			  int sec_channel_offset, int vht_oper_chwidth,
+			  int center_segment0, int center_segment1);
 
 
 #include "drivers/driver.h"
@@ -109,6 +113,9 @@
 			 enum wnm_oper oper, const u8 *peer,
 			 u8 *buf, u16 *buf_len);
 
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+			    u8 qos_map_set_len);
+
 static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
 						  int enabled)
 {
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 2f4ba23..360001f 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -38,6 +38,22 @@
 
 #ifdef NEED_AP_MLME
 
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->bss_load_test_set) {
+		if (2 + 5 > len)
+			return eid;
+		*eid++ = WLAN_EID_BSS_LOAD;
+		*eid++ = 5;
+		os_memcpy(eid, hapd->conf->bss_load_test, 5);
+		eid += 5;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	return eid;
+}
+
+
 static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
 {
 	u8 erp = 0;
@@ -251,6 +267,8 @@
 	/* RSN, MDIE, WPA */
 	pos = hostapd_eid_wpa(hapd, pos, epos - pos);
 
+	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+
 #ifdef CONFIG_IEEE80211N
 	pos = hostapd_eid_ht_capabilities(hapd, pos);
 	pos = hostapd_eid_ht_operation(hapd, pos);
@@ -662,6 +680,9 @@
 	tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
 				  tailpos);
 
+	tailpos = hostapd_eid_bss_load(hapd, tailpos,
+				       tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
 #ifdef CONFIG_IEEE80211N
 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
new file mode 100644
index 0000000..37bbd20
--- /dev/null
+++ b/src/ap/dfs.c
@@ -0,0 +1,601 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "drivers/driver.h"
+#include "dfs.h"
+
+
+static int dfs_get_used_n_chans(struct hostapd_data *hapd)
+{
+	int n_chans = 1;
+
+	if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel)
+		n_chans = 2;
+
+	if (hapd->iconf->ieee80211ac) {
+		switch (hapd->iconf->vht_oper_chwidth) {
+		case VHT_CHANWIDTH_USE_HT:
+			break;
+		case VHT_CHANWIDTH_80MHZ:
+			n_chans = 4;
+			break;
+		case VHT_CHANWIDTH_160MHZ:
+			n_chans = 8;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return n_chans;
+}
+
+
+static int dfs_channel_available(struct hostapd_channel_data *chan)
+{
+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+		return 0;
+	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
+		return 0;
+	return 1;
+}
+
+
+static int dfs_is_ht40_allowed(struct hostapd_channel_data *chan)
+{
+	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+			  184, 192 };
+	unsigned int i;
+
+	for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++) {
+		if (chan->chan == allowed[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int dfs_find_channel(struct hostapd_data *hapd,
+			    struct hostapd_channel_data **ret_chan,
+			    int idx)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan, *next_chan;
+	int i, j, channel_idx = 0, n_chans;
+
+	mode = hapd->iface->current_mode;
+	n_chans = dfs_get_used_n_chans(hapd);
+
+	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		/* Skip not available channels */
+		if (!dfs_channel_available(chan))
+			continue;
+
+		/* Skip HT40/VHT uncompatible channels */
+		if (hapd->iconf->ieee80211n &&
+		    hapd->iconf->secondary_channel) {
+			if (!dfs_is_ht40_allowed(chan))
+				continue;
+
+			for (j = 1; j < n_chans; j++) {
+				next_chan = &mode->channels[i + j];
+				if (!dfs_channel_available(next_chan))
+					break;
+			}
+			if (j != n_chans)
+				continue;
+
+			/* Set HT40+ */
+			hapd->iconf->secondary_channel = 1;
+		}
+
+		if (ret_chan && idx == channel_idx) {
+			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+			*ret_chan = chan;
+			return idx;
+		}
+		wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
+		channel_idx++;
+	}
+	return channel_idx;
+}
+
+
+static void dfs_adjust_vht_center_freq(struct hostapd_data *hapd,
+				       struct hostapd_channel_data *chan)
+{
+	if (!hapd->iconf->ieee80211ac)
+		return;
+
+	if (!chan)
+		return;
+
+	switch (hapd->iconf->vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 2;
+		break;
+	case VHT_CHANWIDTH_80MHZ:
+		hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6;
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		hapd->iconf->vht_oper_centr_freq_seg0_idx =
+						chan->chan + 14;
+	default:
+		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
+		break;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d",
+		   hapd->iconf->vht_oper_centr_freq_seg0_idx);
+}
+
+
+/* Return start channel idx we will use for mode->channels[idx] */
+static int dfs_get_start_chan_idx(struct hostapd_data *hapd)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int channel_no = hapd->iconf->channel;
+	int res = -1, i;
+
+	/* HT40- */
+	if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel == -1)
+		channel_no -= 4;
+
+	/* VHT */
+	if (hapd->iconf->ieee80211ac) {
+		switch (hapd->iconf->vht_oper_chwidth) {
+		case VHT_CHANWIDTH_USE_HT:
+			break;
+		case VHT_CHANWIDTH_80MHZ:
+			channel_no =
+				hapd->iconf->vht_oper_centr_freq_seg0_idx - 6;
+			break;
+		case VHT_CHANWIDTH_160MHZ:
+			channel_no =
+				hapd->iconf->vht_oper_centr_freq_seg0_idx - 14;
+			break;
+		default:
+			wpa_printf(MSG_INFO,
+				   "DFS only VHT20/40/80/160 is supported now");
+			channel_no = -1;
+			break;
+		}
+	}
+
+	/* Get idx */
+	mode = hapd->iface->current_mode;
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+		if (chan->chan == channel_no) {
+			res = i;
+			break;
+		}
+	}
+
+	if (res == -1)
+		wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
+
+	return res;
+}
+
+
+/* At least one channel have radar flag */
+static int dfs_check_chans_radar(struct hostapd_data *hapd, int start_chan_idx,
+				 int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i, res = 0;
+
+	mode = hapd->iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+		if (channel->flag & HOSTAPD_CHAN_RADAR)
+			res++;
+	}
+
+	return res;
+}
+
+
+/* All channels available */
+static int dfs_check_chans_available(struct hostapd_data *hapd,
+				     int start_chan_idx, int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i;
+
+	mode = hapd->iface->current_mode;
+
+	for(i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
+		    HOSTAPD_CHAN_DFS_AVAILABLE)
+			break;
+	}
+
+	return i == n_chans;
+}
+
+
+/* At least one channel unavailable */
+static int dfs_check_chans_unavailable(struct hostapd_data *hapd,
+				       int start_chan_idx,
+				       int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i, res = 0;
+
+	mode = hapd->iface->current_mode;
+
+	for(i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+		if (channel->flag & HOSTAPD_CHAN_DISABLED)
+			res++;
+		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
+		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
+			res++;
+	}
+
+	return res;
+}
+
+
+static struct hostapd_channel_data * dfs_get_valid_channel(
+	struct hostapd_data *hapd)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	int channel_idx, new_channel_idx;
+	u32 _rand;
+
+	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+
+	if (hapd->iface->current_mode == NULL)
+		return NULL;
+
+	mode = hapd->iface->current_mode;
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return NULL;
+
+	/* get random available channel */
+	channel_idx = dfs_find_channel(hapd, NULL, 0);
+	if (channel_idx > 0) {
+		os_get_random((u8 *) &_rand, sizeof(_rand));
+		new_channel_idx = _rand % channel_idx;
+		dfs_find_channel(hapd, &chan, new_channel_idx);
+	}
+
+	/* VHT */
+	dfs_adjust_vht_center_freq(hapd, chan);
+
+	return chan;
+}
+
+
+static int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	int i;
+
+	mode = hapd->iface->current_mode;
+	if (mode == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
+	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+		chan = &hapd->iface->current_mode->channels[i];
+		if (chan->freq == freq) {
+			if (chan->flag & HOSTAPD_CHAN_RADAR) {
+				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+				chan->flag |= state;
+				return 1; /* Channel found */
+			}
+		}
+	}
+	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+	return 0;
+}
+
+
+static int set_dfs_state(struct hostapd_data *hapd, int freq, int ht_enabled,
+			 int chan_offset, int chan_width, int cf1,
+			 int cf2, u32 state)
+{
+	int n_chans = 1, i;
+	struct hostapd_hw_modes *mode;
+	int frequency = freq;
+	int ret = 0;
+
+	mode = hapd->iface->current_mode;
+	if (mode == NULL)
+		return 0;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+		return 0;
+	}
+
+	/* Seems cf1 and chan_width is enough here */
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		n_chans = 1;
+		frequency = cf1;
+		break;
+	case CHAN_WIDTH_40:
+		n_chans = 2;
+		frequency = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		n_chans = 4;
+		frequency = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		n_chans = 8;
+		frequency = cf1 - 70;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+			   chan_width);
+		break;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
+		   n_chans);
+	for (i = 0; i < n_chans; i++) {
+		ret += set_dfs_state_freq(hapd, frequency, state);
+		frequency = frequency + 20;
+	}
+
+	return ret;
+}
+
+
+static int dfs_are_channels_overlapped(struct hostapd_data *hapd, int freq,
+				       int chan_width, int cf1, int cf2)
+{
+	int start_chan_idx;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int n_chans, i, j, frequency = freq, radar_n_chans = 1;
+	u8 radar_chan;
+	int res = 0;
+
+	if (hapd->iface->freq == freq)
+		res++;
+
+	/* Our configuration */
+	mode = hapd->iface->current_mode;
+	start_chan_idx = dfs_get_start_chan_idx(hapd);
+	n_chans = dfs_get_used_n_chans(hapd);
+
+	/* Reported via radar event */
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		radar_n_chans = 1;
+		frequency = cf1;
+		break;
+	case CHAN_WIDTH_40:
+		radar_n_chans = 2;
+		frequency = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		radar_n_chans = 4;
+		frequency = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		radar_n_chans = 8;
+		frequency = cf1 - 70;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+			   chan_width);
+		break;
+	}
+
+	ieee80211_freq_to_chan(frequency, &radar_chan);
+
+	for (i = 0; i < n_chans; i++) {
+		chan = &mode->channels[start_chan_idx + i];
+		for (j = 0; j < radar_n_chans; j++) {
+			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
+				   chan->chan, radar_chan + j * 4);
+			if (chan->chan == radar_chan + j * 4)
+				res++;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
+
+	return res;
+}
+
+
+/*
+ * Main DFS handler
+ * 1 - continue channel/ap setup
+ * 0 - channel/ap setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs(struct hostapd_data *hapd)
+{
+	struct hostapd_channel_data *channel;
+	int res, n_chans, start_chan_idx;
+
+	do {
+		/* Get start (first) channel for current configuration */
+		start_chan_idx = dfs_get_start_chan_idx(hapd);
+		if (start_chan_idx == -1)
+			return -1;
+
+		/* Get number of used channels, depend on width */
+		n_chans = dfs_get_used_n_chans(hapd);
+
+		/* Check if any of configured channels require DFS */
+		res = dfs_check_chans_radar(hapd, start_chan_idx, n_chans);
+		wpa_printf(MSG_DEBUG,
+			   "DFS %d channels required radar detection",
+			   res);
+		if (!res)
+			return 1;
+
+		/* Check if all channels are DFS available */
+		res = dfs_check_chans_available(hapd, start_chan_idx, n_chans);
+		wpa_printf(MSG_DEBUG,
+			   "DFS all channels available, (SKIP CAC): %s",
+			   res ? "yes" : "no");
+		if (res)
+			return 1;
+
+		/* Check if any of configured channels is unavailable */
+		res = dfs_check_chans_unavailable(hapd, start_chan_idx,
+						  n_chans);
+		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
+			   res, res ? "yes": "no");
+		if (res) {
+			channel = dfs_get_valid_channel(hapd);
+			if (!channel) {
+				wpa_printf(MSG_ERROR, "could not get valid channel");
+				return -1;
+			}
+			hapd->iconf->channel = channel->chan;
+			hapd->iface->freq = channel->freq;
+		}
+	} while (res);
+
+	/* Finally start CAC */
+	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", hapd->iface->freq);
+	if (hostapd_start_dfs_cac(hapd, hapd->iconf->hw_mode,
+				  hapd->iface->freq,
+				  hapd->iconf->channel,
+				  hapd->iconf->ieee80211n,
+				  hapd->iconf->ieee80211ac,
+				  hapd->iconf->secondary_channel,
+				  hapd->iconf->vht_oper_chwidth,
+				  hapd->iconf->vht_oper_centr_freq_seg0_idx,
+				  hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
+		wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2)
+{
+	struct hostapd_channel_data *channel;
+	int err = 1;
+
+	if (success) {
+		/* Complete iface/ap configuration */
+		set_dfs_state(hapd, freq, ht_enabled, chan_offset,
+			      chan_width, cf1, cf2,
+			      HOSTAPD_CHAN_DFS_AVAILABLE);
+		hostapd_setup_interface_complete(hapd->iface, 0);
+	} else {
+		/* Switch to new channel */
+		set_dfs_state(hapd, freq, ht_enabled, chan_offset,
+			      chan_width, cf1, cf2,
+			      HOSTAPD_CHAN_DFS_UNAVAILABLE);
+		channel = dfs_get_valid_channel(hapd);
+		if (channel) {
+			hapd->iconf->channel = channel->chan;
+			hapd->iface->freq = channel->freq;
+			err = 0;
+		} else
+			wpa_printf(MSG_ERROR, "No valid channel available");
+
+		hostapd_setup_interface_complete(hapd->iface, err);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd)
+{
+	struct hostapd_channel_data *channel;
+	int err = 1;
+
+	wpa_printf(MSG_DEBUG, "%s called", __func__);
+	channel = dfs_get_valid_channel(hapd);
+	if (channel) {
+		hapd->iconf->channel = channel->chan;
+		hapd->iface->freq = channel->freq;
+		err = 0;
+	}
+
+	hapd->driver->stop_ap(hapd->drv_priv);
+
+	hostapd_setup_interface_complete(hapd->iface, err);
+	return 0;
+}
+
+
+int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq,
+			       int ht_enabled, int chan_offset, int chan_width,
+			       int cf1, int cf2)
+{
+	int res;
+
+	if (!hapd->iconf->ieee80211h)
+		return 0;
+
+	/* mark radar frequency as invalid */
+	res = set_dfs_state(hapd, freq, ht_enabled, chan_offset,
+			    chan_width, cf1, cf2,
+			    HOSTAPD_CHAN_DFS_UNAVAILABLE);
+
+	/* Skip if reported radar event not overlapped our channels */
+	res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2);
+	if (!res)
+		return 0;
+
+	/* we are working on non-DFS channel - skip event */
+	if (res == 0)
+		return 0;
+
+	/* radar detected while operating, switch the channel. */
+	res = hostapd_dfs_start_channel_switch(hapd);
+
+	return res;
+}
+
+
+int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2)
+{
+	/* TODO add correct implementation here */
+	set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
+		      HOSTAPD_CHAN_DFS_USABLE);
+	return 0;
+}
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
new file mode 100644
index 0000000..c9f0578
--- /dev/null
+++ b/src/ap/dfs.h
@@ -0,0 +1,25 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef DFS_H
+#define DFS_H
+
+int hostapd_handle_dfs(struct hostapd_data *hapd);
+
+int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2);
+int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq,
+			       int ht_enabled,
+			       int chan_offset, int chan_width,
+			       int cf1, int cf2);
+int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq,
+			     int ht_enabled,
+			     int chan_offset, int chan_width, int cf1, int cf2);
+
+#endif /* DFS_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index d6bc98d..b30da14 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -29,6 +29,7 @@
 #include "ap_drv_ops.h"
 #include "ap_config.h"
 #include "hw_features.h"
+#include "dfs.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -114,6 +115,13 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_INTERWORKING
+	if (elems.ext_capab && elems.ext_capab_len > 4) {
+		if (elems.ext_capab[4] & 0x01)
+			sta->qos_map_enabled = 1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
 #ifdef CONFIG_HS20
 	wpabuf_free(sta->hs20_ie);
 	if (elems.hs20 && elems.hs20_len > 4) {
@@ -785,6 +793,50 @@
 }
 
 
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+					     struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+	hostapd_dfs_radar_detected(hapd, radar->freq, radar->ht_enabled,
+				   radar->chan_offset, radar->chan_width,
+				   radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+					   struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(hapd, 1, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+					  struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(hapd, 0, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+					   struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+	hostapd_dfs_nop_finished(hapd, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -929,6 +981,34 @@
 	case EVENT_SURVEY:
 		hostapd_event_get_survey(hapd, &data->survey_results);
 		break;
+#ifdef NEED_AP_MLME
+	case EVENT_DFS_RADAR_DETECTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_FINISHED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_ABORTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_NOP_FINISHED:
+		if (!data)
+			break;
+		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+		break;
+	case EVENT_CHANNEL_LIST_CHANGED:
+		/* channel list changed (regulatory?), update channel list */
+		/* TODO: check this. hostapd_get_hw_features() initializes
+		 * too much stuff. */
+		/* hostapd_get_hw_features(hapd->iface); */
+		break;
+#endif /* NEED_AP_MLME */
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 575ef2a..3bca385 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -32,6 +32,7 @@
 #include "ap_config.h"
 #include "p2p_hostapd.h"
 #include "gas_serv.h"
+#include "dfs.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -931,6 +932,9 @@
 				   "be completed in a callback");
 			return 0;
 		}
+
+		if (iface->conf->ieee80211h)
+			wpa_printf(MSG_DEBUG, "DFS support is enabled");
 	}
 	return hostapd_setup_interface_complete(iface, 0);
 }
@@ -950,12 +954,23 @@
 
 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
 	if (hapd->iconf->channel) {
+#ifdef NEED_AP_MLME
+		int res;
+#endif /* NEED_AP_MLME */
+
 		iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
 		wpa_printf(MSG_DEBUG, "Mode: %s  Channel: %d  "
 			   "Frequency: %d MHz",
 			   hostapd_hw_mode_txt(hapd->iconf->hw_mode),
 			   hapd->iconf->channel, iface->freq);
 
+#ifdef NEED_AP_MLME
+		/* Check DFS */
+		res = hostapd_handle_dfs(hapd);
+		if (res <= 0)
+			return res;
+#endif /* NEED_AP_MLME */
+
 		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
 				     hapd->iconf->ieee80211n,
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8a239f4..9e5becc 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -45,6 +45,36 @@
 }
 
 
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+	static char info[256];
+	char *state;
+
+	switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
+	case HOSTAPD_CHAN_DFS_UNKNOWN:
+		state = "unknown";
+		break;
+	case HOSTAPD_CHAN_DFS_USABLE:
+		state = "usable";
+		break;
+	case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+		state = "unavailable";
+		break;
+	case HOSTAPD_CHAN_DFS_AVAILABLE:
+		state = "available";
+		break;
+	default:
+		return "";
+	}
+	os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
+	info[sizeof(info) - 1] = '\0';
+
+	return info;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
 int hostapd_get_hw_features(struct hostapd_iface *iface)
 {
 	struct hostapd_data *hapd = iface->bss[0];
@@ -71,30 +101,40 @@
 
 	for (i = 0; i < num_modes; i++) {
 		struct hostapd_hw_modes *feature = &modes[i];
+		int dfs_enabled = hapd->iconf->ieee80211h &&
+			(iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
 		/* set flag for channels we can use in current regulatory
 		 * domain */
 		for (j = 0; j < feature->num_channels; j++) {
+			int dfs = 0;
+
 			/*
 			 * Disable all channels that are marked not to allow
-			 * IBSS operation or active scanning. In addition,
-			 * disable all channels that require radar detection,
-			 * since that (in addition to full DFS) is not yet
-			 * supported.
+			 * IBSS operation or active scanning.
+			 * Use radar channels only if the driver supports DFS.
 			 */
-			if (feature->channels[j].flag &
-			    (HOSTAPD_CHAN_NO_IBSS |
-			     HOSTAPD_CHAN_PASSIVE_SCAN |
-			     HOSTAPD_CHAN_RADAR))
+			if ((feature->channels[j].flag &
+			     HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+				dfs = 1;
+			} else if (feature->channels[j].flag &
+				   (HOSTAPD_CHAN_NO_IBSS |
+				    HOSTAPD_CHAN_PASSIVE_SCAN |
+				    HOSTAPD_CHAN_RADAR)) {
 				feature->channels[j].flag |=
 					HOSTAPD_CHAN_DISABLED;
+			}
+
 			if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
 				continue;
+
 			wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
-				   "chan=%d freq=%d MHz max_tx_power=%d dBm",
+				   "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
 				   feature->mode,
 				   feature->channels[j].chan,
 				   feature->channels[j].freq,
-				   feature->channels[j].max_tx_power);
+				   feature->channels[j].max_tx_power,
+				   dfs ? dfs_info(&feature->channels[j]) : "");
 		}
 	}
 
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 781f826..c7db7f4 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -859,6 +859,21 @@
 }
 
 
+static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+#ifdef CONFIG_INTERWORKING
+	/* check for QoS Map support */
+	if (ext_capab_ie_len >= 5) {
+		if (ext_capab_ie[4] & 0x01)
+			sta->qos_map_enabled = 1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
 static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			   const u8 *ies, size_t ies_len, int reassoc)
 {
@@ -881,6 +896,9 @@
 	resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
+	resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
 	resp = copy_supp_rates(hapd, sta, &elems);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
@@ -1169,6 +1187,8 @@
 
 	p = hostapd_eid_ext_capab(hapd, p);
 	p = hostapd_eid_bss_max_idle_period(hapd, p);
+	if (sta->qos_map_enabled)
+		p = hostapd_eid_qos_map_set(hapd, p);
 
 	if (sta->flags & WLAN_STA_WMM)
 		p = hostapd_eid_wmm(hapd, p);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2aab56d..61f1316 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -41,6 +41,7 @@
 u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
 			   int probe);
 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);
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index c36bbe3..4172218 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -189,6 +189,8 @@
 			*pos |= 0x80; /* Bit 31 - Interworking */
 		break;
 	case 4: /* Bits 32-39 */
+		if (hapd->conf->qos_map_set_len)
+			*pos |= 0x01; /* Bit 32 - QoS Map */
 		if (hapd->conf->tdls & TDLS_PROHIBIT)
 			*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
 		if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
@@ -250,6 +252,23 @@
 }
 
 
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 len = hapd->conf->qos_map_set_len;
+
+	if (!len)
+		return eid;
+
+	*pos++ = WLAN_EID_QOS_MAP_SET;
+	*pos++ = len;
+	os_memcpy(pos, hapd->conf->qos_map_set, len);
+	pos += len;
+
+	return pos;
+}
+
+
 u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
 {
 	u8 *pos = eid;
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e5b5069..197e46b 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -57,6 +57,7 @@
 	unsigned int no_ht_set:1;
 	unsigned int ht_20mhz_set:1;
 	unsigned int no_p2p_set:1;
+	unsigned int qos_map_enabled:1;
 
 	u16 auth_alg;
 	u8 previous_ap[6];