Patchwork [v2,1/3] hostapd: Add AP DFS support

login
register
mail settings
Submitter Janusz.Dziedzic@tieto.com
Date Oct. 7, 2013, 6:30 a.m.
Message ID <1381127409-2835-1-git-send-email-janusz.dziedzic@tieto.com>
Download mbox | patch
Permalink /patch/280927/
State Accepted
Headers show

Comments

Janusz.Dziedzic@tieto.com - Oct. 7, 2013, 6:30 a.m.
Add DFS structures/events handlers, CAC handling,
radar detection. By default after radar detected or
channel became unavailable random channel
will be choose.

This patches are based on the original work by Boris Presman and
Victor Goldenshtein.

Cc: Boris Presman <boris.presman@ti.com>
Cc: Victor Goldenshtein <victorg@ti.com>

Signed-hostap: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-hostap: Janusz Dziedzic <janusz.dziedzic@tieto.com>
---
 src/ap/ap_drv_ops.c    |   27 +++++++++++++
 src/ap/ap_drv_ops.h    |    1 +
 src/ap/drv_callbacks.c |   77 +++++++++++++++++++++++++++++++++++++
 src/ap/hostapd.c       |   99 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/ap/hostapd.h       |    2 +
 src/ap/hw_features.c   |   78 +++++++++++++++++++++++++++++++++-----
 src/ap/hw_features.h   |    5 +++
 src/ap/ieee802_11.c    |   73 +++++++++++++++++++++++++++++++++++
 src/ap/ieee802_11.h    |    4 +-
 9 files changed, 355 insertions(+), 11 deletions(-)
Jouni Malinen - Oct. 16, 2013, 2:04 p.m.
On Mon, Oct 07, 2013 at 08:30:07AM +0200, Janusz Dziedzic wrote:
> Add DFS structures/events handlers, CAC handling,
> radar detection. By default after radar detected or
> channel became unavailable random channel
> will be choose.
> 
> This patches are based on the original work by Boris Presman and
> Victor Goldenshtein.

Adding these changes in hostapd.c and then removing, re-adding them in
dfs.c, and removing and replacing them yet again in the same patch
series looks a bit odd.. I understand that this is partly because of
different authors being involved and original authors not submitting
this updated version, but the commits in this format make this way too
complex to review and find any issues in changes to functionality.

In addition to that, there were large number of coding style issues that
made this series pretty inconvenient. The patches are too large for that
matter (e.g., with the 3/3 going beyond the limit of this mailing list
size requirements). Renaming of functions make the changes look more
complex than they really were.

I split the changes into smaller commits to make this somewhat more
manageable and also merged some of the add-move-and-move-again parts
into a single commit so that the total number of changes goes down.
This is still far from ideal, but I think we've wasted way too much time
on these as-is, so it is probably better to just commit these and move
on with fixes on top of a fixed baseline.

Since I needed to do significant amount of cleanup, could you please
confirm that the update set of commits are fine? The latest snapshot of
the relevant patches is here: http://w1.fi/p/dfs2/
Janusz.Dziedzic@tieto.com - Oct. 17, 2013, 7:09 a.m.
On 16 October 2013 16:04, Jouni Malinen <j@w1.fi> wrote:
> On Mon, Oct 07, 2013 at 08:30:07AM +0200, Janusz Dziedzic wrote:
>> Add DFS structures/events handlers, CAC handling,
>> radar detection. By default after radar detected or
>> channel became unavailable random channel
>> will be choose.
>>
>> This patches are based on the original work by Boris Presman and
>> Victor Goldenshtein.
>
> Adding these changes in hostapd.c and then removing, re-adding them in
> dfs.c, and removing and replacing them yet again in the same patch
> series looks a bit odd.. I understand that this is partly because of
> different authors being involved and original authors not submitting
> this updated version, but the commits in this format make this way too
> complex to review and find any issues in changes to functionality.
>
> In addition to that, there were large number of coding style issues that
> made this series pretty inconvenient. The patches are too large for that
> matter (e.g., with the 3/3 going beyond the limit of this mailing list
> size requirements). Renaming of functions make the changes look more
> complex than they really were.
>
> I split the changes into smaller commits to make this somewhat more
> manageable and also merged some of the add-move-and-move-again parts
> into a single commit so that the total number of changes goes down.
> This is still far from ideal, but I think we've wasted way too much time
> on these as-is, so it is probably better to just commit these and move
> on with fixes on top of a fixed baseline.
>
> Since I needed to do significant amount of cleanup, could you please
> confirm that the update set of commits are fine? The latest snapshot of
> the relevant patches is here: http://w1.fi/p/dfs2/
>

Yes, I confirm.
We will have more patches (fixes) there, but like you wrote best is
just commit these and apply small fixes next.
Thanks.

BR
Janusz
Jouni Malinen - Oct. 17, 2013, 6:33 p.m.
On Thu, Oct 17, 2013 at 09:09:32AM +0200, Janusz Dziedzic wrote:
> Yes, I confirm.
> We will have more patches (fixes) there, but like you wrote best is
> just commit these and apply small fixes next.

Thanks, I pushed the patches into the master branch.

Patch

diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 3072562..cedfe8e 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -699,3 +699,30 @@  int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
 					 hapd->own_addr, hapd->own_addr, data,
 					 len, 0);
 }
+
+
+int hostapd_start_dfs_cac(struct hostapd_data *hapd, int freq, int flags)
+{
+	if (!hapd->driver || !hapd->driver->start_dfs_cac)
+		return 0;
+
+	if (!(flags & HOSTAPD_CHAN_RADAR)) {
+		wpa_printf(MSG_ERROR, "Can't start DFS_CAC, the channel %u is "
+			   "not DFS channel", hapd->iconf->channel);
+		return -1;
+	}
+
+	if (!hapd->iface->conf->ieee80211h) {
+		wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+			   "is not enabled");
+		return -1;
+	}
+
+	if (hapd->iface->conf->secondary_channel) {
+		wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+			   "on HT40 is not supported");
+		return -1;
+	}
+
+	return hapd->driver->start_dfs_cac(hapd->drv_priv, freq);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 23277b6..d56aa91 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -101,6 +101,7 @@  int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
 		      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 freq, int flags);
 
 
 #include "drivers/driver.h"
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index d6bc98d..68c12d2 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -785,6 +785,56 @@  static void hostapd_event_get_survey(struct hostapd_data *hapd,
 }
 
 
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+					     struct dfs_event *radar)
+{
+	int res;
+	wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+
+	if (!hapd->iconf->ieee80211h)
+		return;
+
+	/* mark radar frequency as invalid */
+	res = ieee802_11_set_dfs_state(hapd, radar->freq,
+				       HOSTAPD_CHAN_DFS_UNAVAILABLE);
+
+	/* other frequency, just mark it and return. */
+	if (hapd->iface->freq != radar->freq)
+		return;
+
+	/* we are working on non-DFS channel - skip event */
+	if (res == 0)
+		return;
+
+	/* radar detected while operating, switch the channel. */
+	ieee802_11_start_channel_switch(hapd);
+}
+
+
+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);
+	ieee802_11_complete_cac(hapd, 1, radar->freq);
+}
+
+
+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);
+	ieee802_11_complete_cac(hapd, 0, radar->freq);
+}
+
+
+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);
+	ieee802_11_set_dfs_state(hapd, radar->freq, HOSTAPD_CHAN_DFS_USABLE);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -929,6 +979,33 @@  void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 	case EVENT_SURVEY:
 		hostapd_event_get_survey(hapd, &data->survey_results);
 		break;
+	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;
+
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 575ef2a..37379f8 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -931,6 +931,9 @@  static int setup_interface(struct hostapd_iface *iface)
 				   "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);
 }
@@ -941,6 +944,9 @@  int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t j;
 	u8 *prev_addr;
+	int flags;
+	struct hostapd_channel_data *channel;
+
 
 	if (err) {
 		wpa_printf(MSG_ERROR, "Interface initialization failed");
@@ -956,6 +962,41 @@  int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
 			   hostapd_hw_mode_txt(hapd->iconf->hw_mode),
 			   hapd->iconf->channel, iface->freq);
 
+		/* Handle DFS channel */
+check_dfs_chan_again:
+		flags = hostapd_hw_get_channel_flag(hapd, hapd->iconf->channel);
+		if (flags & HOSTAPD_CHAN_RADAR) {
+			switch(flags & HOSTAPD_CHAN_DFS_MASK) {
+			case HOSTAPD_CHAN_DFS_USABLE:
+				wpa_printf(MSG_DEBUG, "DFS start CAC on %dMHz", iface->freq);
+				if (hostapd_start_dfs_cac(hapd, hapd->iface->freq, flags)) {
+					wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed\n");
+					return -1;
+				}
+				/* Continue initialisation after CAC */
+				return 0;
+				break;
+			case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+				wpa_printf(MSG_DEBUG, "HOSTAPD_CHAN_DFS_UNAVAILABLE, get new chan");
+				/* find other channel here */
+				channel = hostapd_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;
+				goto check_dfs_chan_again;
+				break;
+			case HOSTAPD_CHAN_DFS_AVAILABLE:
+				/* We don't need CAC here */
+				wpa_printf(MSG_DEBUG, "HOSTAPD_CHAN_DFS_AVAILABLE, skip CAC");
+				break;
+			default:
+				break;
+			}
+		}
+
 		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
 				     hapd->iconf->ieee80211n,
@@ -1478,3 +1519,61 @@  void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
 			       ap_handle_timer, hapd, sta);
 }
+
+static int hostapd_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;
+	int i, channel_idx = 0;
+
+	mode = hapd->iface->current_mode;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		if (chan->flag & HOSTAPD_CHAN_RADAR &&
+		    chan->flag & HOSTAPD_CHAN_DFS_UNAVAILABLE)
+			continue;
+
+		if (ret_chan && idx == channel_idx) {
+			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+			*ret_chan = chan;
+			return idx;
+		}
+		channel_idx++;
+	}
+	return channel_idx;
+}
+
+struct hostapd_channel_data * hostapd_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 = hostapd_dfs_find_channel(hapd, NULL, 0);
+	if (channel_idx > 0) {
+		os_get_random((u8 *) &_rand, sizeof(_rand));
+		new_channel_idx = _rand % channel_idx;
+		hostapd_dfs_find_channel(hapd, &chan, new_channel_idx);
+	};
+
+	return chan;
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dbf1b52..379dd8d 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -380,4 +380,6 @@  const struct hostapd_eap_user *
 hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
 		     size_t identity_len, int phase2);
 
+struct hostapd_channel_data * hostapd_dfs_get_valid_channel(
+						struct hostapd_data *hapd);
 #endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8a239f4..c3e2080 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -45,6 +45,36 @@  void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
 }
 
 
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+	static char info[256], *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 @@  int hostapd_get_hw_features(struct hostapd_iface *iface)
 
 	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]) : "");
 		}
 	}
 
@@ -849,3 +889,21 @@  int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 
 	return 0;
 }
+
+
+int hostapd_hw_get_channel_flag(struct hostapd_data *hapd, int chan)
+{
+	int i;
+
+	if (!hapd->iface->current_mode)
+		return 0;
+
+	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+		struct hostapd_channel_data *ch =
+			&hapd->iface->current_mode->channels[i];
+		if (ch->chan == chan)
+			return ch->flag;
+	}
+
+	return 0;
+}
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index abadcd1..fe18d7c 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -28,6 +28,7 @@  int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
 int hostapd_check_ht_capab(struct hostapd_iface *iface);
 int hostapd_prepare_rates(struct hostapd_iface *iface,
 			  struct hostapd_hw_modes *mode);
+int hostapd_hw_get_channel_flag(struct hostapd_data *hapd, int chan);
 #else /* NEED_AP_MLME */
 static inline void
 hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -66,6 +67,10 @@  static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
 	return 0;
 }
 
+static inline int hostapd_get_channel_flag(struct hostapd_data *hapd, int chan)
+{
+	return 0;
+}
 #endif /* NEED_AP_MLME */
 
 #endif /* HW_FEATURES_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 781f826..52bb5d0 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2239,5 +2239,78 @@  void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
 			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
 }
 
+int ieee802_11_set_dfs_state(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;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+		return 0;
+	}
+
+	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;
+}
+
+int ieee802_11_complete_cac(struct hostapd_data *hapd, int success, int freq)
+{
+	struct hostapd_channel_data *channel;
+	int err = 1;
+
+	if (success) {
+		/* Complete iface/ap configuration */
+		ieee802_11_set_dfs_state(hapd, freq, HOSTAPD_CHAN_DFS_AVAILABLE);
+		hostapd_setup_interface_complete(hapd->iface, 0);
+	} else {
+		/* Switch to new channel */
+		ieee802_11_set_dfs_state(hapd, freq, HOSTAPD_CHAN_DFS_UNAVAILABLE);
+		channel = hostapd_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;
+}
+
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd)
+{
+	struct hostapd_channel_data *channel;
+	wpa_printf(MSG_DEBUG, "%s called\n", __func__);
+	int err = 1;
+
+	channel = hostapd_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;
+}
 
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2aab56d..358edfd 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -80,5 +80,7 @@  u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
 int hostapd_update_time_adv(struct hostapd_data *hapd);
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
 u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
-
+int ieee802_11_complete_cac(struct hostapd_data *hapd, int success, int freq);
+int ieee802_11_set_dfs_state(struct hostapd_data *hapd, int freq, u32 state);
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd);
 #endif /* IEEE802_11_H */