2012-06-18 14:52:59

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 0/7] hostap: add DFS master ability

In continuation to proposed RFC, this patch set (with nl/cfg/mac80211 patch series) adds support for DFS (Dynamic Frequency Selection) according 802.11h.


Main idea
=========

DFS master algorithm is implemented in the hostapd, while nl/cfg/mac80211 will pipe relevant commands/events to the driver/hostapd.
Based on the assumption that the device/driver supports radar interference detection i.e., it is capable to generate radar_detected event by using different pattern detection techniques:

1. Pattern detection in the HW: the device generates 'radar_detected' event.
2. Pattern detection in the driver: the driver receives radar pulses from the device and generates 'radar detected' event.


Main DFS procedures
===================

1. Hostapd gets driver's dfs and channel switch capabilities.
2. If 80211h is enabled in the hostapd.conf, the driver supports one of the above radar detection techniques and supports an AP channel switch, hostapd may use DFS channels.
3. Hostapd selects an operational channel (default from hostapd.conf), if selected channel is a DFS channel, hostapd sends start_radar_detection command to the device/driver which starts monitoring for radar interference while hostapd sets a timer for a CAC (Channel Availability Check) time, which is 60 seconds.
4. As CAC timer expires and no radar has been detected, hostapd may continue with the init flow, otherwise if interference is detected hostapd selects another channel (random selection) and repeats the CAC on the new channel (in case the new channel is also a DFS channel), while the original channel is added to a "black list" for a period of ''No-Occupancy'' time (time that the channel can't be used/selected).
5. While using the channel the device/driver continuously monitors for potential radar interference. If interference is detected hostapd is notified with 'radar detected' event, which selects a new channel and triggers a channel switch procedure, if the new channel is also a DFS channel, hostapd performs the CAC test, once it's successfully passed hostapd instructs the driver to initiate the transmission on the channel.


Updates & changes from the proposed RFC
=======================================

1. In addition to nl/cfg80211 safeguards which ensures that no TX will occur on DFS channels prior performing a valid channel availability check, added additional protection to hostap which now saves a time stamp of the last start radar detection command and prior enabling the tx on the DFS channels will validate whether DFS_MIN_CAC_TIME (60 seconds) has passed since the beginning of the CAC.
2. Changed the Signed-off-by to Signed-hostap.
3. Added sanity checks in drv ops such as “ if the channel is DFS, if the DFS is supported/enabled”
4. Removed “eloop_enable()”, instead just restore “eloop.terminate” state at the end of eloop_run().
5. Filter out irrelevant events during DFS init phase.
6. Added AP channel switch capability check, if this capability is not available DFS cannot be started.
7. Removed “channel” parameter from “struct hostapd_channel_switch”.


Victor Goldenshtein (7):
hostapd: implement dfs drv ops functions
hostapd: add channel switch ability
hostapd: add dfs events
hostapd: add dfs support into interface init flow
nl80211: add support to enable TX on oper-channel
nl80211: add channel switch command/event
nl80211: add start radar detection command/event

hostapd/config_file.c | 10 +++
src/ap/ap_config.h | 4 +
src/ap/ap_drv_ops.c | 83 ++++++++++++++++++++++
src/ap/ap_drv_ops.h | 5 ++
src/ap/beacon.c | 16 ++++
src/ap/drv_callbacks.c | 47 ++++++++++++
src/ap/hostapd.c | 161 ++++++++++++++++++++++++++++++++++++++++--
src/ap/hostapd.h | 12 +++
src/ap/hw_features.c | 48 ++++++++++---
src/ap/hw_features.h | 10 +++
src/ap/ieee802_11.c | 117 ++++++++++++++++++++++++++++++
src/ap/ieee802_11.h | 4 +
src/drivers/driver.h | 91 +++++++++++++++++++++++-
src/drivers/driver_common.c | 2 +
src/drivers/driver_nl80211.c | 129 +++++++++++++++++++++++++++++++++
src/utils/eloop.c | 1 +
16 files changed, 723 insertions(+), 17 deletions(-)

--
1.7.5.4



2012-06-18 14:53:25

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 6/7] nl80211: add channel switch command/event

Implement AP channel switch command and handle
channel switch complete event.

Signed-hostap: Boris Presman <[email protected]>
Signed-hostap: Victor Goldenshtein <[email protected]>
---
src/drivers/driver_nl80211.c | 54 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 54 insertions(+), 0 deletions(-)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index fe6e4d2..7e8b800 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -2054,6 +2054,8 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
int cmd, struct nlattr **tb)
{
+ union wpa_event_data data;
+
if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
(cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
cmd == NL80211_CMD_SCAN_ABORTED)) {
@@ -2161,6 +2163,17 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
case NL80211_CMD_PROBE_CLIENT:
nl80211_client_probe_event(drv, tb);
break;
+ case NL80211_CMD_AP_CHANNEL_SWITCH:
+ os_memset(&data, 0, sizeof(data));
+ if (tb[NL80211_ATTR_WIPHY_FREQ]) {
+ data.channel_switch_complete.freq =
+ nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]);
+ }
+ data.channel_switch_complete.status = TRUE;
+ wpa_printf(MSG_DEBUG, "nl80211: Channel switch complete event");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_SWITCH_COMPLETE,
+ &data);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
@@ -8805,6 +8818,46 @@ nla_put_failure:
}


+static int nl80211_ap_channel_switch(void *priv,
+ struct hostapd_channel_switch *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Channel switch, "
+ "ch_switch_count = %d, tx_block = %d, "
+ "freq = %d, post_switch_block_tx = %d.",
+ params->ch_switch_count, params->tx_block,
+ params->freq, params->post_switch_block_tx);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_AP_CHANNEL_SWITCH);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, params->ch_switch_count);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+
+ if (params->tx_block)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX);
+
+ if (params->post_switch_block_tx)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_POST_BLOCK_TX);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to channel switch: "
+ "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
#ifdef CONFIG_TDLS

static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
@@ -9100,6 +9153,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.poll_client = nl80211_poll_client,
.set_p2p_powersave = nl80211_set_p2p_powersave,
.enable_tx = nl80211_enable_dfs_tx,
+ .hapd_channel_switch = nl80211_ap_channel_switch,
#ifdef CONFIG_TDLS
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
--
1.7.5.4


2012-06-18 14:53:07

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 3/7] hostapd: add dfs events

Add EVENT_RADAR_DETECTED and EVENT_CHANNEL_SWITCH_COMPLETE
events.

EVENT_RADAR_DETECTED indicates that radar was detected on
operational channel. EVENT_CHANNEL_SWITCH_COMPLETE indicates
that device has finished the channel switch process.

Signed-hostap: Boris Presman <[email protected]>
Signed-hostap: Victor Goldenshtein <[email protected]>
---
src/ap/drv_callbacks.c | 47 +++++++++++++++++++++++++++++++++++++++++++
src/drivers/driver.h | 22 +++++++++++++++++++-
src/drivers/driver_common.c | 2 +
3 files changed, 70 insertions(+), 1 deletions(-)

diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 4c0d0ab..603820f 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -469,6 +469,37 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
ieee802_1x_receive(hapd, src, data, data_len);
}

+static void hostapd_event_radar_detected(struct hostapd_data *hapd,
+ struct radar_detected *radar)
+{
+ if (!hapd->iconf->ieee80211h)
+ return;
+
+ if ((!(hapd->iface->dfs_state & DFS_INIT_PHASE_CAC)) &&
+ (hapd->iface->freq != radar->freq)) {
+ wpa_printf(MSG_WARNING, "False radar detection, op_freq(%d) != "
+ "radar_freq(%d)", hapd->iface->freq, radar->freq);
+ return;
+ }
+
+ if (ieee802_11_radar_detected(hapd))
+ ieee802_11_start_channel_switch(hapd, TRUE);
+ else
+ wpa_printf(MSG_DEBUG, "False radar detection");
+}
+
+static void hostapd_event_complete_channel_switch(struct hostapd_data *hapd,
+ struct channel_switch_complete *channel_switch)
+{
+ if (hapd->next_channel->freq != channel_switch->freq) {
+ wpa_printf(MSG_WARNING, "Switched to wrong freq, next_freq(%d) "
+ "!= ch_switch_freq(%d)", hapd->next_channel->freq,
+ channel_switch->freq);
+ }
+
+ if (channel_switch->status)
+ ieee802_11_complete_channel_switch(hapd);
+}

void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
@@ -492,6 +523,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
event_to_string(event), event);
#endif /* CONFIG_NO_STDOUT_DEBUG */

+ if ((hapd->iface->dfs_state & DFS_INIT_PHASE_CAC) &&
+ (event != EVENT_RADAR_DETECTED)) {
+ wpa_dbg(hapd->msg_ctx, level, "Irrelevant event "
+ "during DFS init phase");
+ return;
+ }
+
switch (event) {
case EVENT_MICHAEL_MIC_FAILURE:
michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
@@ -582,6 +620,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
break;
+ case EVENT_RADAR_DETECTED:
+ if (data)
+ hostapd_event_radar_detected(hapd, &data->radar_detected);
+ break;
+ case EVENT_CHANNEL_SWITCH_COMPLETE:
+ if (data)
+ hostapd_event_complete_channel_switch(hapd,
+ &data->channel_switch_complete);
+ break;
#ifdef NEED_AP_MLME
case EVENT_RX_ACTION:
if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 780fac8..44f6af0 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -3042,7 +3042,17 @@ enum wpa_event_type {
/**
* EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
*/
- EVENT_EAPOL_TX_STATUS
+ EVENT_EAPOL_TX_STATUS,
+
+ /**
+ * EVENT_RADAR_DETECTED - notify of radar detection
+ */
+ EVENT_RADAR_DETECTED,
+
+ /**
+ * EVENT_CHANNEL_SWITCH_COMPLETE - notify of channel switch complete
+ */
+ EVENT_CHANNEL_SWITCH_COMPLETE
};


@@ -3637,6 +3647,16 @@ union wpa_event_data {
int data_len;
int ack;
} eapol_tx_status;
+
+ struct radar_detected {
+ int status;
+ int freq;
+ } radar_detected;
+
+ struct channel_switch_complete {
+ int status;
+ int freq;
+ } channel_switch_complete;
};

/**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 345e851..321b057 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -77,6 +77,8 @@ const char * event_to_string(enum wpa_event_type event)
E2S(SCHED_SCAN_STOPPED);
E2S(DRIVER_CLIENT_POLL_OK);
E2S(EAPOL_TX_STATUS);
+ E2S(RADAR_DETECTED);
+ E2S(CHANNEL_SWITCH_COMPLETE);
}

return "UNKNOWN";
--
1.7.5.4


2012-06-18 14:58:43

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 4/7] hostapd: add dfs support into interface init flow

Implement Channel Availability Check (CAC) during initialization
phase. According to DFS requirements the AP should monitor 'radar
channels' for potential radar interference for a minimum CAC time
prior enabling the transmissions.

Add dfs support into hw features, allow the usage of the radar
channels if ieee80211h is enabled in the hostapd.conf and the
driver supports radar detection with AP channel switch ability.

Signed-hostap: Boris Presman <[email protected]>
Signed-hostap: Victor Goldenshtein <[email protected]>
---
src/ap/hostapd.c | 16 ++++++++++------
src/ap/hw_features.c | 30 ++++++++++++++++++++----------
src/ap/hw_features.h | 9 +++++++++
src/drivers/driver.h | 5 +++++
src/drivers/driver_nl80211.c | 9 +++++++++
src/utils/eloop.c | 1 +
6 files changed, 54 insertions(+), 16 deletions(-)

diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 01f083e..dd5dc45 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -772,6 +772,12 @@ static int setup_interface(struct hostapd_iface *iface)
"be completed in a callback");
return 0;
}
+
+ if (iface->current_mode->dfs_supported &&
+ iface->conf->ieee80211h) {
+ wpa_printf(MSG_DEBUG, "DFS support is enabled");
+ iface->dfs_state |= DFS_ENABLED;
+ }
}
return hostapd_setup_interface_complete(iface, 0);
}
@@ -797,14 +803,12 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
hostapd_hw_mode_txt(hapd->iconf->hw_mode),
hapd->iconf->channel, iface->freq);

- if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
- hapd->iconf->channel,
- hapd->iconf->ieee80211n,
- hapd->iconf->secondary_channel)) {
- wpa_printf(MSG_ERROR, "Could not set channel for "
- "kernel driver");
+ if (hostapd_check_set_freq(hapd)) {
+ wpa_printf(MSG_ERROR, "Couldn't check/set freq");
return -1;
}
+
+ wpa_printf(MSG_DEBUG, "Continuing with init flow");
}

if (iface->current_mode) {
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index bbd2b03..3fc43eb 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -70,26 +70,36 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)

for (i = 0; i < num_modes; i++) {
struct hostapd_hw_modes *feature = &modes[i];
+ u8 dfs_enabled = (hapd->iconf->ieee80211h &&
+ feature->dfs_supported);
/* set flag for channels we can use in current regulatory
* domain */
for (j = 0; j < feature->num_channels; j++) {
+ u8 dfs = FALSE;
/*
* 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 = TRUE;
+ } else if (feature->channels[j].flag &
+ (HOSTAPD_CHAN_NO_IBSS |
+ HOSTAPD_CHAN_PASSIVE_SCAN |
+ HOSTAPD_CHAN_DISABLED)) {
feature->channels[j].flag |=
HOSTAPD_CHAN_DISABLED;
- if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+ }
+
+ if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) {
continue;
- wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
+ }
+
+ wpa_printf(MSG_WARNING, "Allowed %schannel: mode=%d "
"chan=%d freq=%d MHz max_tx_power=%d dBm",
+ dfs ? "(DFS) " : "",
feature->mode,
feature->channels[j].chan,
feature->channels[j].freq,
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index b8e287b..67bab44 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -46,6 +46,11 @@ static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
return -100;
}

+static inline int hostapd_select_radar_detector(struct hostapd_iface *iface)
+{
+ return -1;
+}
+
static inline const char * hostapd_hw_mode_txt(int mode)
{
return NULL;
@@ -67,6 +72,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/drivers/driver.h b/src/drivers/driver.h
index 44f6af0..daf66f1 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -130,6 +130,11 @@ struct hostapd_hw_modes {
u8 a_mpdu_params;

unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
+
+ /**
+ * dfs_supported - DFS radar detection is supported in driver/HW.
+ */
+ u8 dfs_supported;
};


diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 92a7de0..f6a0c98 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -4730,6 +4730,15 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
*(phy_info->num_modes) += 1;

+ if (tb_msg[NL80211_ATTR_FEATURE_FLAGS]) {
+ u32 drv_feature_flags =
+ nla_get_u32(tb_msg[NL80211_ATTR_FEATURE_FLAGS]);
+ mode->dfs_supported = ((drv_feature_flags &
+ NL80211_FEATURE_DFS) &&
+ (drv_feature_flags &
+ NL80211_FEATURE_AP_CH_SWITCH));
+ }
+
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
nla_len(nl_band), NULL);

diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 5691f15..89611b7 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -774,6 +774,7 @@ void eloop_run(void)
}

out:
+ eloop.terminate = 0;
#ifndef CONFIG_ELOOP_POLL
os_free(rfds);
os_free(wfds);
--
1.7.5.4


2012-06-18 14:53:34

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 7/7] nl80211: add start radar detection command/event

Add command to trigger radar detection in the driver/HW.
Once radar detection has started, the driver/device shall
continuously monitor the Operating Channel (In-Service
Monitoring) to ensure that there is no radar interferences
on that channel. Process radar detection event in
do_process_drv_event().

Signed-hostap: Victor Goldenshtein <[email protected]>
---
src/drivers/driver_nl80211.c | 38 ++++++++++++++++++++++++++++++++++++++
1 files changed, 38 insertions(+), 0 deletions(-)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 7e8b800..74d4310 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -2174,6 +2174,16 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_SWITCH_COMPLETE,
&data);
break;
+ case NL80211_CMD_RADAR_DETECT:
+ os_memset(&data, 0, sizeof(data));
+ if (tb[NL80211_ATTR_WIPHY_FREQ]) {
+ data.radar_detected.freq =
+ nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]);
+ }
+ data.radar_detected.status = TRUE;
+ wpa_printf(MSG_DEBUG, "nl80211: Radar detected event");
+ wpa_supplicant_event(drv->ctx, EVENT_RADAR_DETECTED, &data);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
@@ -8858,6 +8868,33 @@ nla_put_failure:
}


+static int nl80211_start_radar_detection(void *priv, int freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Start radar detection");
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
+ "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
#ifdef CONFIG_TDLS

static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
@@ -9154,6 +9191,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_p2p_powersave = nl80211_set_p2p_powersave,
.enable_tx = nl80211_enable_dfs_tx,
.hapd_channel_switch = nl80211_ap_channel_switch,
+ .start_radar_detection = nl80211_start_radar_detection,
#ifdef CONFIG_TDLS
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
--
1.7.5.4


2012-06-18 14:52:56

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 1/7] hostapd: implement dfs drv ops functions

Add nl80211 driver operation functions:
hostapd_enable_tx – to enable tx on DFS channel after
channel availability check.
hostapd_channel_switch – to handle channel switch request.
hostapd_start_radar_detection – to enable radar detection
on DFS channel.

Signed-hostap: Victor Goldenshtein <[email protected]>
---
src/ap/ap_config.h | 3 ++
src/ap/ap_drv_ops.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/ap/ap_drv_ops.h | 5 +++
src/ap/hostapd.h | 6 +++
src/drivers/driver.h | 48 +++++++++++++++++++++++++++++
5 files changed, 145 insertions(+), 0 deletions(-)

diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 78c9068..8b18b4e 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -430,6 +430,9 @@ struct hostapd_config {

int ieee80211d;

+ /* DFS */
+ int channel_switch_count;
+
struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];

/*
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 859b529..40c3132 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -596,3 +596,86 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
hapd->own_addr, hapd->own_addr, data,
len, 0);
}
+
+int hostapd_enable_tx(struct hostapd_data *hapd, int freq, int flags)
+{
+ struct os_time now;
+
+ if (!hapd->driver || !hapd->driver->enable_tx)
+ return 0;
+
+ if ((flags & HOSTAPD_CHAN_RADAR) &&
+ !(hapd->iface->dfs_state & DFS_ENABLED)) {
+ wpa_printf(MSG_ERROR, "Can't enable tx, "
+ "DFS functionality is not enabled");
+ return -1;
+ }
+
+ os_get_time(&now);
+
+ if ((flags & HOSTAPD_CHAN_RADAR) &&
+ now.sec - hapd->iface->dfs_start_cac_ts < DFS_MIN_CAC_TIME_SEC) {
+ wpa_printf(MSG_ERROR, "Can't enable tx, "
+ "DFS min CAC time hasn't passed");
+ return -1;
+ }
+
+ hapd->iface->dfs_start_cac_ts = 0;
+
+ return hapd->driver->enable_tx(hapd->drv_priv, freq);
+}
+
+
+int hostapd_channel_switch(struct hostapd_data *hapd, int freq, int flags,
+ u8 tx_block, u8 post_switch_block_tx)
+{
+ struct hostapd_channel_switch params;
+
+ if (!hapd->driver || !hapd->driver->hapd_channel_switch)
+ return 0;
+
+ if ((flags & HOSTAPD_CHAN_RADAR) &&
+ !(hapd->iface->dfs_state & DFS_ENABLED)) {
+ wpa_printf(MSG_ERROR, "Can't switch to radar channel, "
+ "DFS functionality is not enabled");
+ return -1;
+ }
+
+ params.freq = freq;
+ params.tx_block = tx_block;
+ params.post_switch_block_tx = post_switch_block_tx;
+ params.ch_switch_count = hapd->iface->conf->channel_switch_count;
+
+ return hapd->driver->hapd_channel_switch(hapd->drv_priv, &params);
+}
+
+int hostapd_start_radar_detection(struct hostapd_data *hapd, int freq,
+ int flags)
+{
+ int ret;
+ struct os_time now;
+
+ if (!hapd->driver || !hapd->driver->start_radar_detection)
+ return 0;
+
+ if (!(flags & HOSTAPD_CHAN_RADAR)) {
+ wpa_printf(MSG_ERROR, "Can't start radar detection, the channel"
+ " %d is not DFS channel", (int)hapd->iconf->channel);
+ return -1;
+ }
+
+ if (!(hapd->iface->dfs_state & DFS_ENABLED)) {
+ wpa_printf(MSG_ERROR, "Can't start radar detection, "
+ "DFS functionality is not enabled");
+ return -1;
+ }
+
+ ret = hapd->driver->start_radar_detection(hapd->drv_priv, freq);
+ if (!ret) {
+ os_get_time(&now);
+ hapd->iface->dfs_start_cac_ts = now.sec;
+ } else
+ hapd->iface->dfs_start_cac_ts = 0;
+
+ return ret;
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 169c91b..0f60d3c 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -97,6 +97,11 @@ 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_enable_tx(struct hostapd_data *hapd, int freq, int flags);
+int hostapd_channel_switch(struct hostapd_data *hapd, int freq, int flags,
+ u8 tx_block, u8 post_switch_block_tx);
+int hostapd_start_radar_detection(struct hostapd_data *hapd, int freq,
+ int flags);


#include "drivers/driver.h"
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 4e45d59..fe153f7 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -240,6 +240,12 @@ struct hostapd_iface {
int olbc_ht;

u16 ht_op_mode;
+
+ /* dfs states */
+ u8 dfs_state;
+ /* dfs start channel availability check time stamp */
+ os_time_t dfs_start_cac_ts;
+
void (*scan_cb)(struct hostapd_iface *iface);

int (*ctrl_iface_init)(struct hostapd_data *hapd);
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 102ea46..0a809fe 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -29,6 +29,19 @@
#define HOSTAPD_CHAN_HT40MINUS 0x00000020
#define HOSTAPD_CHAN_HT40 0x00000040

+/*
+ * DFS functionality is enabled.
+ */
+#define DFS_ENABLED BIT(0)
+
+/*
+ * DFS Channel Availability Check Time - the time a system shall monitor a
+ * 'radar channel' for presence of radar prior to initiating a TX, spec defines
+ * it as 60 seconds.
+ * Driver will block any attempt to enable the TX prior this minimal CAC time.
+ */
+#define DFS_MIN_CAC_TIME_SEC 60
+
/**
* struct hostapd_channel_data - Channel information
*/
@@ -878,6 +891,16 @@ struct hostapd_freq_params {
* enabled, secondary channel above primary */
};

+struct hostapd_channel_switch {
+ int freq;
+ int tx_block; /* immediately block the tx on the
+ * operational channel
+ * (prior channel switch) */
+ int post_switch_block_tx; /* block tx on the target ch (after
+ * channel switch) */
+ int ch_switch_count;
+};
+
enum wpa_driver_if_type {
/**
* WPA_IF_STATION - Station mode interface
@@ -2548,6 +2571,31 @@ struct wpa_driver_ops {
* avoid frequency conflict in single channel concurrency.
*/
int (*switch_channel)(void *priv, unsigned int freq);
+
+ /**
+ * hapd_channel_switch - Perform an AP channel switch
+ * @priv: Private driver interface data
+ * @params: Channels switch parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*hapd_channel_switch)(void *priv,
+ struct hostapd_channel_switch *params);
+
+ /**
+ * enable_tx - Enable TX
+ * @priv: Private driver interface data
+ * @freq: Frequency (in MHz) of the channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*enable_tx)(void *priv, int freq);
+
+ /**
+ * start_radar_detection - Listen for radar interference on the channel.
+ * @priv: Private driver interface data
+ * @freq: Frequency (in MHz) of the channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*start_radar_detection)(void *priv, int freq);
};


--
1.7.5.4


2012-06-18 14:53:18

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 5/7] nl80211: add support to enable TX on oper-channel

Once a channel has been passes the channel availability
check (CAC) the ap may start the tx with
wpa_driver_nl80211_enable_dfs_tx().

Add wpa_driver_nl80211_enable_dfs_tx() + init drv_ops.

Signed-hostap: Boris Presman <[email protected]>
Signed-hostap: Victor Goldenshtein <[email protected]>
---
src/drivers/driver_nl80211.c | 28 ++++++++++++++++++++++++++++
1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index f6a0c98..fe6e4d2 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -8778,6 +8778,33 @@ static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
}


+static int nl80211_enable_dfs_tx(void *priv, int freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Enable DFS tx");
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_DFS_ENABLE_TX);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to enable DFS tx: "
+ "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
#ifdef CONFIG_TDLS

static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
@@ -9072,6 +9099,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_rekey_info = nl80211_set_rekey_info,
.poll_client = nl80211_poll_client,
.set_p2p_powersave = nl80211_set_p2p_powersave,
+ .enable_tx = nl80211_enable_dfs_tx,
#ifdef CONFIG_TDLS
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
--
1.7.5.4


2012-06-18 14:53:01

by Victor Goldenshtein

[permalink] [raw]
Subject: [PATCH 2/7] hostapd: add channel switch ability

Add channel switch command and handle channel switch
complete event.

New hostapd_eid_csa() which builds the channel switch
announcement IE. Add this CSA to the beacon frame prior
performing a channel switch and remove it once it's
completed.

Set WLAN_CAPABILITY_SPECTRUM_MGMT bit in the capability
information field in beacon frames and probe response
frames.

Signed-hostap: Boris Presman <[email protected]>
Signed-hostap: Victor Goldenshtein <[email protected]>
---
hostapd/config_file.c | 10 ++++
src/ap/ap_config.h | 1 +
src/ap/beacon.c | 16 ++++++
src/ap/hostapd.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++
src/ap/hostapd.h | 6 ++
src/ap/hw_features.c | 18 ++++++
src/ap/hw_features.h | 1 +
src/ap/ieee802_11.c | 117 +++++++++++++++++++++++++++++++++++++++
src/ap/ieee802_11.h | 4 ++
src/drivers/driver.h | 16 ++++++
10 files changed, 334 insertions(+), 0 deletions(-)

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 6729e5c..063fc22 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1239,6 +1239,12 @@ static int hostapd_config_check(struct hostapd_config *conf)
return -1;
}

+ if (conf->ieee80211h && (!conf->ieee80211d)) {
+ wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
+ "IEEE 802.11d enabled");
+ return -1;
+ }
+
for (i = 0; i < conf->num_bss; i++) {
if (hostapd_config_check_bss(&conf->bss[i], conf))
return -1;
@@ -1423,6 +1429,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->country[2] = ' ';
} else if (os_strcmp(buf, "ieee80211d") == 0) {
conf->ieee80211d = atoi(pos);
+ } else if (os_strcmp(buf, "ieee80211h") == 0) {
+ conf->ieee80211h = atoi(pos);
+ } else if (os_strcmp(buf, "channel_switch_count") == 0) {
+ conf->channel_switch_count = atoi(pos);
} else if (os_strcmp(buf, "ieee8021x") == 0) {
bss->ieee802_1x = atoi(pos);
} else if (os_strcmp(buf, "eapol_version") == 0) {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 8b18b4e..7947156 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -432,6 +432,7 @@ struct hostapd_config {

/* DFS */
int channel_switch_count;
+ int ieee80211h;

struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];

diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index b711063..deb906f 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -171,6 +171,19 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
return pos;
}

+static u8 *hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+ if (!(hapd->iconf->ieee80211h) || !(hapd->next_channel))
+ return eid;
+
+ *eid++ = WLAN_EID_CHANNEL_SWITCH;
+ *eid++ = 3; /* IE length */
+ *eid++ = 1; /* STAs should cease transmit */
+ *eid++ = (u8)hapd->next_channel->chan;
+ *eid++ = (u8)hapd->iconf->channel_switch_count;
+ return eid;
+}
+

static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
{
@@ -568,6 +581,9 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
tailpos = hostapd_eid_country(hapd, tailpos,
tail + BEACON_TAIL_BUF_SIZE - tailpos);

+ /* Channel Switch Announcement */
+ tailpos = hostapd_eid_csa(hapd, tailpos);
+
/* ERP Information element */
tailpos = hostapd_eid_erp_info(hapd, tailpos);

diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 9d6fd7b..01f083e 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1018,3 +1018,148 @@ 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);
}
+
+struct hostapd_channel_data *hostapd_dfs_get_valid_channel(struct hostapd_data
+ *hapd)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int i, channel_idx = 0, new_channel_idx;
+ struct os_time now;
+ u32 rand;
+
+ wpa_printf(MSG_DEBUG, "Selecting next channel");
+
+ if (hapd->iface->current_mode == NULL)
+ return NULL;
+
+ mode = hapd->iface->current_mode;
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return NULL;
+
+ os_get_time(&now);
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ /*
+ * Reactivate the disabled channels after
+ * Non-Occupancy Period.
+ */
+ if (chan->flag & HOSTAPD_CHAN_RADAR_DETECTED) {
+ if ((chan->last_radar_detection.sec +
+ DFS_MIN_NON_OCC_TIME_SEC) < now.sec) {
+ wpa_printf(MSG_DEBUG, "Activating channel %d.",
+ chan->chan);
+ chan->flag &= ~HOSTAPD_CHAN_RADAR_DETECTED;
+ chan->last_radar_detection.sec = 0;
+ chan->last_radar_detection.usec = 0;
+ } else
+ continue;
+ }
+ channel_idx++;
+ }
+
+ os_get_random((u8 *)&rand, sizeof(rand));
+ new_channel_idx = rand % channel_idx;
+
+ for (i = 0, channel_idx = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR_DETECTED))
+ continue;
+ if (channel_idx == new_channel_idx) {
+ wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+ return chan;
+ }
+ channel_idx++;
+ }
+
+ return NULL;
+}
+
+void hostapd_resume_dfs_cac(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ int flag;
+
+ wpa_printf(MSG_DEBUG, "Resuming DFS channel availability check");
+
+ flag = hostapd_hw_get_channel_flag(hapd, hapd->iconf->channel);
+
+ /*
+ * We get here after successful CAC (channel availability check) during:
+ * 1. Initialization: in this case we continue with the init flow.
+ * 2. Operational mode: system switched to a DFS channel (probably due
+ * to radar event), which requires to perform a CAC prior enabling the
+ * tx on the new channel.
+ */
+
+ if (hapd->iface->dfs_state & DFS_INIT_PHASE_CAC) {
+ eloop_terminate();
+ hapd->iface->dfs_state &= ~DFS_INIT_PHASE_CAC;
+ }
+
+ /* Enable TX on operational channel */
+ hostapd_enable_tx(hapd, hapd->iface->freq, flag);
+}
+
+int hostapd_check_set_freq(struct hostapd_data *hapd)
+{
+ int flag;
+
+ wpa_printf(MSG_DEBUG, "Check and set freq %d, channel %d",
+ hapd->iface->freq, hapd->iconf->channel);
+
+ flag = hostapd_hw_get_channel_flag(hapd, hapd->iconf->channel);
+
+ if ((flag & HOSTAPD_CHAN_RADAR) &&
+ !(hapd->iface->dfs_state & DFS_ENABLED)) {
+ wpa_printf(MSG_ERROR, "Can't set DFS frequency, "
+ "DFS functionality is not enabled/supported");
+ return -1;
+ }
+
+ if (hostapd_set_freq(hapd, hapd->iconf->hw_mode,
+ hapd->iface->freq,
+ hapd->iconf->channel,
+ hapd->iconf->ieee80211n,
+ hapd->iconf->secondary_channel)) {
+ wpa_printf(MSG_ERROR, "Could not set channel for "
+ "kernel driver");
+ return -1;
+ }
+
+ if (flag & HOSTAPD_CHAN_RADAR) {
+ if (hostapd_start_radar_detection(hapd, hapd->iface->freq,
+ flag)) {
+ wpa_printf(MSG_ERROR, "Could not start "
+ "radar detection for kernel driver");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "CAC, listen %d seconds for radar "
+ "interference", DFS_MIN_CAC_TIME_SEC);
+
+ eloop_register_timeout(DFS_MIN_CAC_TIME_SEC, 0,
+ hostapd_resume_dfs_cac, hapd, NULL);
+
+ /*
+ * Listen for radar interference for CAC period, eloop_run()
+ * will be terminated after CAC timeout.
+ */
+ if (!(hapd->iface->dfs_state & DFS_INIT_PHASE_CAC)) {
+ hapd->iface->dfs_state |= DFS_INIT_PHASE_CAC;
+ eloop_run();
+ }
+
+ } else if (hapd->iface->dfs_state & DFS_INIT_PHASE_CAC) {
+ wpa_printf(MSG_DEBUG, "moved to non-DFS channel");
+ hapd->iface->dfs_state &= ~DFS_INIT_PHASE_CAC;
+ hostapd_enable_tx(hapd, hapd->iface->freq, flag);
+ eloop_terminate();
+ }
+ return 0;
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index fe153f7..e405ebc 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -156,6 +156,8 @@ struct hostapd_data {
void (*setup_complete_cb)(void *ctx);
void *setup_complete_cb_ctx;

+ struct hostapd_channel_data *next_channel;
+
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -271,6 +273,8 @@ void hostapd_interface_deinit(struct hostapd_iface *iface);
void hostapd_interface_free(struct hostapd_iface *iface);
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc);
+struct hostapd_channel_data *hostapd_dfs_get_valid_channel(struct hostapd_data
+ *hapd);

/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -289,5 +293,7 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal);
+void hostapd_resume_dfs_cac(void *eloop_ctx, void *timeout_ctx);
+int hostapd_check_set_freq(struct hostapd_data *hapd);

#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 76c4211..bbd2b03 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -794,3 +794,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..b8e287b 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,
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 3996c90..2269cd5 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -153,6 +153,9 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
hapd->iface->num_sta_no_short_slot_time == 0)
capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;

+ if (hapd->iconf->ieee80211h)
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
return capab;
}

@@ -1849,4 +1852,118 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
}


+int ieee802_11_radar_detected(struct hostapd_data *hapd)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan = NULL;
+ struct os_time now;
+ int i;
+
+ eloop_cancel_timeout(hostapd_resume_dfs_cac, hapd, NULL);
+
+ if (hapd->iface->current_mode == NULL)
+ return 0;
+
+ mode = hapd->iface->current_mode;
+ 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 == hapd->iface->freq) {
+ if (chan->flag & HOSTAPD_CHAN_RADAR) {
+ chan->flag |= HOSTAPD_CHAN_RADAR_DETECTED;
+ os_get_time(&now);
+ chan->last_radar_detection.sec = now.sec;
+ wpa_printf(MSG_DEBUG, "Disabling channel %d, "
+ "for %d seconds", chan->chan,
+ DFS_MIN_NON_OCC_TIME_SEC);
+ return 1; /* Channel found */
+ }
+ }
+ }
+ wpa_printf(MSG_WARNING, "Should'n get a radar event on "
+ "the current freq (%d)", hapd->iface->freq);
+ return 0;
+}
+
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd,
+ u8 radar_detected)
+{
+ struct hostapd_channel_data *next_channel;
+
+ next_channel = hostapd_dfs_get_valid_channel(hapd);
+
+ if (!next_channel)
+ return -1;
+
+ if (!(hapd->iface->dfs_state & DFS_INIT_PHASE_CAC)) {
+ hapd->next_channel = next_channel;
+ u8 radar_on_next_channel =
+ (next_channel->flag & HOSTAPD_CHAN_RADAR) ? 1 : 0;
+ wpa_printf(MSG_DEBUG, "switching to %sch. #%d, freq %d",
+ radar_on_next_channel ? "(DFS) " : "",
+ next_channel->chan, next_channel->freq);
+
+ /* Add CSA */
+ ieee802_11_set_beacon(hapd);
+
+ if (hostapd_channel_switch(hapd, next_channel->freq,
+ next_channel->flag,
+ radar_detected,
+ radar_on_next_channel)) {
+ wpa_printf(MSG_ERROR, "Channel switch failed");
+ return -1;
+ }
+ } else {
+ hapd->iconf->channel = next_channel->chan;
+ hapd->iface->freq = next_channel->freq;
+
+ if (hostapd_check_set_freq(hapd)) {
+ wpa_printf(MSG_ERROR, "Couldn't check/set freq, "
+ "terminating");
+ eloop_terminate();
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int ieee802_11_complete_channel_switch(struct hostapd_data *hapd)
+{
+
+ wpa_printf(MSG_DEBUG, "Completing channel switch");
+
+ if (hapd->next_channel == NULL) {
+ wpa_printf(MSG_WARNING, "next_channel is not defined");
+ return 0;
+ }
+
+ hapd->iconf->channel = (u8)hapd->next_channel->chan;
+ hapd->iface->freq = hapd->next_channel->freq;
+
+ if (hapd->next_channel->flag & HOSTAPD_CHAN_RADAR) {
+ if (hostapd_start_radar_detection(hapd, hapd->iface->freq,
+ hapd->next_channel->flag)) {
+ wpa_printf(MSG_ERROR, "Could not start radar "
+ "detection for kernel driver");
+ return -1;
+ }
+ eloop_register_timeout(DFS_MIN_CAC_TIME_SEC, 0,
+ hostapd_resume_dfs_cac, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "listen %d seconds for radar "
+ "interference prior enabling the tx",
+ DFS_MIN_CAC_TIME_SEC);
+ }
+
+ hapd->next_channel = NULL;
+ /* Remove CSA */
+ ieee802_11_set_beacon(hapd);
+
+ return 0;
+}
+
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index b60350f..1a2867b 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -73,5 +73,9 @@ 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_radar_detected(struct hostapd_data *hapd);
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd,
+ u8 radar_detected);
+int ieee802_11_complete_channel_switch(struct hostapd_data *hapd);

#endif /* IEEE802_11_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 0a809fe..780fac8 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -28,6 +28,7 @@
#define HOSTAPD_CHAN_HT40PLUS 0x00000010
#define HOSTAPD_CHAN_HT40MINUS 0x00000020
#define HOSTAPD_CHAN_HT40 0x00000040
+#define HOSTAPD_CHAN_RADAR_DETECTED 0x00000080

/*
* DFS functionality is enabled.
@@ -35,6 +36,11 @@
#define DFS_ENABLED BIT(0)

/*
+ * DFS channel availability check during initialization.
+ */
+#define DFS_INIT_PHASE_CAC BIT(1)
+
+/*
* DFS Channel Availability Check Time - the time a system shall monitor a
* 'radar channel' for presence of radar prior to initiating a TX, spec defines
* it as 60 seconds.
@@ -42,6 +48,14 @@
*/
#define DFS_MIN_CAC_TIME_SEC 60

+/*
+ * DFS Non-Occupancy Time - a period of time after radar is detected on a
+ * channel that the channel may not be used.
+ *
+ */
+#define DFS_MIN_NON_OCC_TIME_SEC 1800
+
+
/**
* struct hostapd_channel_data - Channel information
*/
@@ -65,6 +79,8 @@ struct hostapd_channel_data {
* max_tx_power - maximum transmit power in dBm
*/
u8 max_tx_power;
+
+ struct os_time last_radar_detection;
};

#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
--
1.7.5.4