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

Janusz Dziedzic janusz.dziedzic
Sun Oct 6 23:30:07 PDT 2013


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 at ti.com>
Cc: Victor Goldenshtein <victorg at ti.com>

Signed-hostap: Simon Wunderlich <siwu at hrz.tu-chemnitz.de>
Signed-hostap: Janusz Dziedzic <janusz.dziedzic at 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(-)

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 */
-- 
1.7.9.5




More information about the Hostap mailing list