From ff0a677405533513777ed413abff97483f5fda6d Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Mon, 21 Nov 2011 15:19:52 +0200 Subject: hostap: add channel switch ability to AP & GO (INTERNAL) Add channel switch command and handle channel switch request/complete events. 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. New EVENT_REQ_CH_SW which indicates that the driver has requested to perform a channel switch. Signed-hostap: Victor Goldenshtein --- hostapd/config_file.c | 2 ++ src/ap/beacon.c | 25 +++++++++++++++++++++++++ src/ap/drv_callbacks.c | 33 +++++++++++++++++++++++++++++++-- src/ap/hostapd.c | 31 +++++++++++++++++++++++++++++++ src/ap/hostapd.h | 5 +++++ src/ap/hw_features.c | 18 ++++++++++++++++++ src/ap/hw_features.h | 1 + src/ap/ieee802_11.c | 30 ++++++++++++++++++++++++++++++ src/drivers/driver.h | 9 ++++++++- src/drivers/driver_common.c | 1 + wpa_supplicant/events.c | 6 ++++++ 11 files changed, 158 insertions(+), 3 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 03f82b2..59745fa 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1441,6 +1441,8 @@ 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, "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/beacon.c b/src/ap/beacon.c index a1bb067..72c3ead 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -33,6 +33,7 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "beacon.h" +#include "hw_features.h" #ifdef NEED_AP_MLME @@ -171,6 +172,27 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, return pos; } +static u8 *hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) +{ + int current_ch_flag = 0; + + if (!hapd->next_channel) + return eid; + + current_ch_flag = hostapd_hw_get_channel_flag(hapd, + hapd->iconf->channel); + + *eid++ = WLAN_EID_CHANNEL_SWITCH; + *eid++ = 3; /* IE length */ + /* STAs should cease transmit if the switch is due to radar */ + *eid++ = (current_ch_flag & HOSTAPD_CHAN_RADAR) ? 1 : 0; + *eid++ = (u8)hapd->next_channel->chan; + *eid++ = (hapd->iconf->channel_switch_count > hapd->conf->dtim_period) ? + (u8)hapd->iconf->channel_switch_count : + hapd->conf->dtim_period * 2; + return eid; +} + static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) { @@ -573,6 +595,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/drv_callbacks.c b/src/ap/drv_callbacks.c index f80a08c..7e478a5 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -27,6 +27,7 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "hw_features.h" +#include "beacon.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -286,8 +287,6 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, HOSTAPD_LEVEL_INFO, "driver had channel switch: " "freq=%d, ht=%d, offset=%d", freq, ht, offset); - hapd->iface->freq = freq; - channel = hostapd_hw_get_channel(hapd, freq); if (!channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, @@ -299,6 +298,30 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; hapd->iconf->secondary_channel = offset; + + + if (hapd->iface->freq == freq) + return; + + hapd->iface->freq = freq; + + if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, freq, + hapd->iconf->channel, + hapd->iconf->ieee80211n, + hapd->iconf->secondary_channel)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "failed to update the freq !"); + return; + } + + hapd->next_channel = NULL; + + /* update the DS IE */ + ieee802_11_set_beacon(hapd); + + wpa_printf(MSG_DEBUG, "AP/GO moved to channel %d", channel); + #endif /* NEED_AP_MLME */ } @@ -627,6 +650,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->ch_switch.ht_enabled, data->ch_switch.ch_offset); break; + case EVENT_REQ_CH_SW: + if (!data) + break; + ieee802_11_start_channel_switch(hapd, data->ch_switch.freq, + FALSE); + break; default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 3406cd1..4e06808 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1106,3 +1106,34 @@ 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_get_valid_channel(struct hostapd_data *hapd, + int req_freq) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int i, channel_idx = 0; + + wpa_printf(MSG_DEBUG, "Selecting next channel"); + + if (hapd->iface->current_mode == NULL) + return NULL; + + mode = hapd->iface->current_mode; + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_RADAR)) + continue; + channel_idx++; + + /* request specific channel */ + if (req_freq && (req_freq == chan->freq)) + return chan; + } + + wpa_printf(MSG_WARNING, "Could't get requested channel"); + return NULL; +} diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 0465844..feea42d 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -162,6 +162,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,9 @@ 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_get_valid_channel(struct hostapd_data *hapd, + int req_freq); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 0900e78..930d35b 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -796,3 +796,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 ed1661a..0b23561 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1889,4 +1889,34 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, } +int ieee802_11_start_channel_switch(struct hostapd_data *hapd, + int freq, u8 radar_detected) +{ + struct hostapd_channel_data *next_channel; + + next_channel = hostapd_get_valid_channel(hapd, freq); + if (!next_channel) + return -1; + + hapd->next_channel = next_channel; + u8 radar_on_next_channel = + (next_channel->flag & HOSTAPD_CHAN_RADAR) ? 1 : 0; + wpa_printf(MSG_INFO, "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; + } + return 0; +} + + #endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index b25b4b9..6446593 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -3060,7 +3060,12 @@ enum wpa_event_type { * * Described in wpa_event_data.ch_switch * */ - EVENT_CH_SWITCH + EVENT_CH_SWITCH, + + /** + * EVENT_REQ_CH_SW - a request to perform a channel switch for GO/AP + */ + EVENT_REQ_CH_SW }; @@ -3723,6 +3728,8 @@ static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, /* driver_common.c */ void wpa_scan_results_free(struct wpa_scan_results *res); +int ieee802_11_start_channel_switch(struct hostapd_data *hapd, + int freq, u8 radar_detected); /* Convert wpa_event_type to a string for logging */ const char * event_to_string(enum wpa_event_type event); diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 5e4dc89..a0ada97 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -82,6 +82,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(ROAMING_ENABLED); E2S(ROAMING_DISABLED); E2S(START_ROAMING); + E2S(REQ_CH_SW); } return "UNKNOWN"; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 56ae94c..c3e9ed6 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2556,6 +2556,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->ch_switch.ch_offset); #endif /* CONFIG_AP */ break; + case EVENT_REQ_CH_SW: + if (!data) + break; + ieee802_11_start_channel_switch(wpa_s->ap_iface->bss[0], + data->ch_switch.freq, FALSE); + break; case EVENT_RX_MGMT: { u16 fc, stype; const struct ieee80211_mgmt *mgmt; -- cgit v1.1