From 8d1fdde7f0e2e1a7c5614cafe41bafde0d9ab9e3 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 25 Nov 2013 20:16:12 +0100 Subject: [PATCH] nl80211/hostapd: Extend channel switch notify handling Adds support for VHT by parsing bandwidth and center_freq{1,2}. Signed-hostap: Michal Kazior Signed-hostap: Janusz Dziedzic --- src/ap/drv_callbacks.c | 46 ++++++++++++-- src/ap/hostapd.h | 2 +- src/drivers/driver.h | 6 ++ src/drivers/driver_nl80211.c | 113 ++++++++++++++++++++++------------- wpa_supplicant/ap.c | 4 +- wpa_supplicant/ap.h | 2 +- wpa_supplicant/events.c | 5 +- 7 files changed, 127 insertions(+), 51 deletions(-) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 1b69ba826..b066bffdb 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -381,14 +381,15 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, - int offset) + int offset, int width, int cf1, int cf2) { #ifdef NEED_AP_MLME - int channel; + int channel, chwidth, seg0_idx = 0, seg1_idx = 0; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "driver had channel switch: " - "freq=%d, ht=%d, offset=%d", freq, ht, offset); + "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d", + freq, ht, offset, width, cf1, cf2); hapd->iface->freq = freq; @@ -400,9 +401,43 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, return; } + switch (width) { + case CHAN_WIDTH_80: + chwidth = VHT_CHANWIDTH_80MHZ; + break; + case CHAN_WIDTH_80P80: + chwidth = VHT_CHANWIDTH_80P80MHZ; + break; + case CHAN_WIDTH_160: + chwidth = VHT_CHANWIDTH_160MHZ; + break; + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + default: + chwidth = VHT_CHANWIDTH_USE_HT; + break; + } + + switch (hapd->iface->current_mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + if (cf1 > 5000) + seg0_idx = (cf1 - 5000) / 5; + if (cf2 > 5000) + seg1_idx = (cf2 - 5000) / 5; + break; + default: + seg0_idx = hostapd_hw_get_channel(hapd, cf1); + seg1_idx = hostapd_hw_get_channel(hapd, cf2); + break; + } + hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; hapd->iconf->secondary_channel = offset; + hapd->iconf->vht_oper_chwidth = chwidth; + hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; + hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; if (hapd->iface->csa_in_progress && freq == hapd->iface->cs_freq) { hostapd_cleanup_cs_params(hapd); @@ -976,7 +1011,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; hostapd_event_ch_switch(hapd, data->ch_switch.freq, data->ch_switch.ht_enabled, - data->ch_switch.ch_offset); + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); break; case EVENT_CONNECT_FAILED_REASON: if (!data) diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index c7ec7dc3c..84468de00 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -414,7 +414,7 @@ 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_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, - int offset); + int offset, int width, int cf1, int cf2); const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 3502eb805..a3602ed78 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -4028,11 +4028,17 @@ union wpa_event_data { * @freq: Frequency of new channel in MHz * @ht_enabled: Whether this is an HT channel * @ch_offset: Secondary channel offset + * @ch_width: Channel width + * @cf1: Center frequency 1 + * @cf2: Center frequency 2 */ struct ch_switch { int freq; int ht_enabled; int ch_offset; + enum chan_width ch_width; + int cf1; + int cf2; } ch_switch; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 64ab29a63..42dddf04a 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -504,6 +504,27 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd) } +/* Converts nl80211_chan_width to a common format */ +static enum chan_width convert2width(int width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return CHAN_WIDTH_20_NOHT; + case NL80211_CHAN_WIDTH_20: + return CHAN_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return CHAN_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return CHAN_WIDTH_80; + case NL80211_CHAN_WIDTH_80P80: + return CHAN_WIDTH_80P80; + case NL80211_CHAN_WIDTH_160: + return CHAN_WIDTH_160; + } + return CHAN_WIDTH_UNKNOWN; +} + + static int is_ap_interface(enum nl80211_iftype nlmode) { return (nlmode == NL80211_IFTYPE_AP || @@ -1484,36 +1505,60 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, struct nlattr *type) + struct nlattr *ifindex, struct nlattr *freq, + struct nlattr *type, struct nlattr *bw, + struct nlattr *cf1, struct nlattr *cf2) { + struct i802_bss *bss; union wpa_event_data data; int ht_enabled = 1; int chan_offset = 0; + int ifidx; wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); - if (!freq || !type) + if (!freq) return; - switch (nla_get_u32(type)) { - case NL80211_CHAN_NO_HT: - ht_enabled = 0; - break; - case NL80211_CHAN_HT20: - break; - case NL80211_CHAN_HT40PLUS: - chan_offset = 1; - break; - case NL80211_CHAN_HT40MINUS: - chan_offset = -1; - break; + ifidx = nla_get_u32(ifindex); + for (bss = drv->first_bss; bss; bss = bss->next) + if (bss->ifindex == ifidx) + break; + + if (bss == NULL) { + wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", + ifidx); + return; } + if (type) { + switch (nla_get_u32(type)) { + case NL80211_CHAN_NO_HT: + ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + chan_offset = -1; + break; + } + } + + os_memset(&data, 0, sizeof(data)); data.ch_switch.freq = nla_get_u32(freq); data.ch_switch.ht_enabled = ht_enabled; data.ch_switch.ch_offset = chan_offset; + if (bw) + data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); + if (cf1) + data.ch_switch.cf1 = nla_get_u32(cf1); + if (cf2) + data.ch_switch.cf2 = nla_get_u32(cf2); - drv->first_bss->freq = data.ch_switch.freq; + bss->freq = data.ch_switch.freq; wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); } @@ -2534,8 +2579,6 @@ static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, } -static enum chan_width convert2width(int width); - static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { @@ -2702,8 +2745,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_RESP_IE]); break; case NL80211_CMD_CH_SWITCH_NOTIFY: - mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ], - tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + mlme_event_ch_switch(drv, + tb[NL80211_ATTR_IFINDEX], + tb[NL80211_ATTR_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], + tb[NL80211_ATTR_CHANNEL_WIDTH], + tb[NL80211_ATTR_CENTER_FREQ1], + tb[NL80211_ATTR_CENTER_FREQ2]); break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], @@ -10020,27 +10068,6 @@ nla_put_failure: } -/* Converts nl80211_chan_width to a common format */ -static enum chan_width convert2width(int width) -{ - switch (width) { - case NL80211_CHAN_WIDTH_20_NOHT: - return CHAN_WIDTH_20_NOHT; - case NL80211_CHAN_WIDTH_20: - return CHAN_WIDTH_20; - case NL80211_CHAN_WIDTH_40: - return CHAN_WIDTH_40; - case NL80211_CHAN_WIDTH_80: - return CHAN_WIDTH_80; - case NL80211_CHAN_WIDTH_80P80: - return CHAN_WIDTH_80P80; - case NL80211_CHAN_WIDTH_160: - return CHAN_WIDTH_160; - } - return CHAN_WIDTH_UNKNOWN; -} - - static int get_channel_width(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -11288,9 +11315,11 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) struct nlattr *beacon_csa; int ret = -ENOBUFS; - wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d)", + wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)", settings->cs_count, settings->block_tx, - settings->freq_params.freq); + settings->freq_params.freq, settings->freq_params.bandwidth, + settings->freq_params.center_freq1, + settings->freq_params.center_freq2); if (!drv->channel_switch_supported) { wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command"); diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index cbe67a4a8..394ab30d0 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -1085,13 +1085,13 @@ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, - int offset) + int offset, int width, int cf1, int cf2) { if (!wpa_s->ap_iface) return; wpa_s->assoc_freq = freq; - hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset); + hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1); } diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 33a3d0f75..c3828980d 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -54,7 +54,7 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s, struct csa_settings *settings); int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr); void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, - int offset); + int offset, int width, int cf1, int cf2); struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef); #ifdef CONFIG_AP diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index aacf5b960..517d812f0 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2891,7 +2891,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, data->ch_switch.ht_enabled, - data->ch_switch.ch_offset); + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); break; #endif /* CONFIG_AP */ #if defined(CONFIG_AP) || defined(CONFIG_IBSS_RSN)