@@ -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);
+}
@@ -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"
@@ -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;
@@ -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;
+}
@@ -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 */
@@ -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;
+}
@@ -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 */
@@ -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 */
@@ -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 */